*****************************************************************************/
@interface VLCPlaylist ()
{
- playlist_item_t * p_current_root_item;
-
NSImage *o_descendingSortingImage;
NSImage *o_ascendingSortingImage;
[o_columnArray release];
}
-- (playlist_item_t *)currentPlaylistRoot
-{
- // TODO remove
- playlist_t *p_playlist = pl_Get(VLCIntf);
- return p_playlist->p_playing;
-}
-
- (PLModel *)model
{
return o_model;
[o_outline_view setRowHeight:rowHeight];
}
-- (id)init
-{
- self = [super init];
- if (self != nil) {
-
-
- playlist_t * p_playlist = pl_Get(VLCIntf);
- p_current_root_item = p_playlist->p_local_category;
- o_outline_dict = [[NSMutableDictionary alloc] init];
- }
- return self;
-}
-
- (void)dealloc
{
- [o_outline_dict release];
[super dealloc];
}
return;
playlist_t * p_playlist = pl_Get(VLCIntf);
- [o_outline_view setTarget: self];
- [o_outline_view setDelegate: self];
- [o_outline_view setAllowsEmptySelection: NO];
- [o_outline_view expandItem: [o_outline_view itemAtRow:0]];
[self reloadStyles];
[self initStrings];
- o_model = [[PLModel alloc] initWithOutlineView:o_outline_view playlist:p_playlist rootItem:p_current_root_item playlistObject:self];
+ o_model = [[PLModel alloc] initWithOutlineView:o_outline_view playlist:p_playlist rootItem:p_playlist->p_playing playlistObject:self];
[o_outline_view setDataSource:o_model];
[o_outline_view reloadData];
+ [o_outline_view setTarget: self];
[o_outline_view setDoubleAction: @selector(playItem:)];
+ [o_outline_view setAllowsEmptySelection: NO];
[o_outline_view registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
[o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
[o_mi_dl_cover_art setTitle: _NS("Download Cover Art")];
[o_mi_preparse setTitle: _NS("Fetch Meta Data")];
[o_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
- [o_mm_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
- [[o_mm_mi_revealInFinder menu] setAutoenablesItems: NO];
[o_mi_sort_name setTitle: _NS("Sort Node by Name")];
[o_mi_sort_author setTitle: _NS("Sort Node by Author")];
- (void)playlistUpdated
{
- /* Clear indications of any existing column sorting */
- NSUInteger count = [[o_outline_view tableColumns] count];
- for (NSUInteger i = 0 ; i < count ; i++)
- [o_outline_view setIndicatorImage:nil inTableColumn: [[o_outline_view tableColumns] objectAtIndex:i]];
-
- [o_outline_view setHighlightedTableColumn:nil];
- o_tc_sortColumn = nil;
- // TODO Find a way to keep the dict size to a minimum
- //[o_outline_dict removeAllObjects];
[o_outline_view reloadData];
- [[[[VLCMain sharedInstance] wizard] playlistWizard] reloadOutlineView];
-
- [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:retainedRowSelection] byExtendingSelection:NO];
-
- [self outlineViewSelectionDidChange: nil];
- [[VLCMain sharedInstance] updateMainWindow];
}
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
return [o_outline_view selectedRow] == -1;
}
-- (void)updateRowSelection
+- (void)currentlyPlayingItemChanged
{
- // FIXME: unsafe
- playlist_t *p_playlist = pl_Get(VLCIntf);
- playlist_item_t *p_item, *p_temp_item;
- NSMutableArray *o_array = [NSMutableArray array];
-
- PL_LOCK;
- p_item = playlist_CurrentPlayingItem(p_playlist);
- if (p_item == NULL) {
- PL_UNLOCK;
+ PLItem *item = [[self model] currentlyPlayingItem];
+ if (!item)
return;
- }
-
- p_temp_item = p_item;
- while(p_temp_item->p_parent) {
- [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
- p_temp_item = p_temp_item->p_parent;
- }
- PL_UNLOCK;
- NSUInteger count = [o_array count];
- for (NSUInteger j = 0; j < count - 1; j++) {
- id o_item;
- if ((o_item = [o_outline_dict objectForKey:
- [NSString stringWithFormat: @"%p",
- [[o_array objectAtIndex:j] pointerValue]]]) != nil) {
- [o_outline_view expandItem: o_item];
+ // select item
+ NSInteger itemIndex = [o_outline_view rowForItem:item];
+ if (itemIndex < 0) {
+ // expand if needed
+ while (item != nil) {
+ PLItem *parent = [item parent];
+
+ if (![o_outline_view isExpandable: parent])
+ break;
+ if (![o_outline_view isItemExpanded: parent])
+ [o_outline_view expandItem: parent];
+ item = parent;
}
- }
-
- id o_item = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_item]];
- NSInteger i_index = [o_outline_view rowForItem:o_item];
- [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_index] byExtendingSelection:NO];
- [o_outline_view setNeedsDisplay:YES];
-}
-
-/* Check if p_item is a child of p_node recursively. We need to check the item
- existence first since OSX sometimes tries to redraw items that have been
- deleted. We don't do it when not required since this verification takes
- quite a long time on big playlists (yes, pretty hacky). */
-// todo remove useless parameters
-- (BOOL)isItem: (PLItem *)p_item inNode: (PLItem *)p_node checkItemExistence:(BOOL)b_check locked:(BOOL)b_locked
-{
- PLItem *p_temp_item = p_item;
-
- if ([p_node plItemId] == [p_item plItemId])
- return YES;
-
- while(p_temp_item) {
- p_temp_item = [p_temp_item parent];
- if ([p_temp_item plItemId] == [p_node plItemId]) {
- return YES;
+ // search for row again
+ itemIndex = [o_outline_view rowForItem:item];
+ if (itemIndex < 0) {
+ return;
}
}
- return NO;
+ [o_outline_view selectRowIndexes: [NSIndexSet indexSetWithIndex: itemIndex] byExtendingSelection: NO];
}
- (IBAction)savePlaylist:(id)sender
/* When called retrieves the selected outlineview row and plays that node or item */
- (IBAction)playItem:(id)sender
{
- intf_thread_t * p_intf = VLCIntf;
- playlist_t * p_playlist = pl_Get(p_intf);
-
- playlist_item_t *p_item;
- playlist_item_t *p_node = NULL;
+ playlist_t *p_playlist = pl_Get(VLCIntf);
// ignore clicks on column header when handling double action
- if (sender != nil && [o_outline_view clickedRow] == -1 && sender != o_mi_play)
+ if (sender == o_outline_view && [o_outline_view clickedRow] == -1)
return;
- PL_LOCK;
PLItem *o_item = [o_outline_view itemAtRow:[o_outline_view selectedRow]];
- p_item = playlist_ItemGetById(p_playlist, [o_item plItemId]);
+ if (!o_item)
+ return;
- if (p_item) {
- if (p_item->i_children == -1) {
- p_node = p_item->p_parent;
- } else {
- p_node = p_item;
- if (p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1)
- p_item = p_node->pp_children[0];
- else
- p_item = NULL;
- }
+ PL_LOCK;
+ playlist_item_t *p_item = playlist_ItemGetById(p_playlist, [o_item plItemId]);
+ playlist_item_t *p_node = playlist_ItemGetById(p_playlist, [[[self model] rootItem] plItemId]);
+ if (p_item && p_node) {
playlist_Control(p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item);
}
PL_UNLOCK;
- (IBAction)revealItemInFinder:(id)sender
{
- NSIndexSet * selectedRows = [o_outline_view selectedRowIndexes];
- NSUInteger count = [selectedRows count];
- NSUInteger indexes[count];
- [selectedRows getIndexes:indexes maxCount:count inIndexRange:nil];
-
- NSMutableString * o_mrl;
- for (NSUInteger i = 0; i < count; i++) {
- PLItem *o_item = [o_outline_view itemAtRow:indexes[i]];
+ NSIndexSet *selectedRows = [o_outline_view selectedRowIndexes];
+ [selectedRows enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
- char * psz_url = decode_URI(input_item_GetURI([o_item input]));
- o_mrl = [[NSMutableString alloc] initWithString: [NSString stringWithUTF8String:psz_url ? psz_url : ""]];
- if (psz_url != NULL)
- free( psz_url );
+ PLItem *o_item = [o_outline_view itemAtRow:idx];
/* perform some checks whether it is a file and if it is local at all... */
- if ([o_mrl length] > 0) {
- NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
- if (prefix_range.location != NSNotFound)
- [o_mrl deleteCharactersInRange: prefix_range];
+ char *psz_url = input_item_GetURI([o_item input]);
+ NSURL *url = [NSURL URLWithString:toNSStr(psz_url)];
+ free(psz_url);
+ if (![url isFileURL])
+ return;
+ if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]])
+ return;
- if ([o_mrl characterAtIndex:0] == '/')
- [[NSWorkspace sharedWorkspace] selectFile: o_mrl inFileViewerRootedAtPath: o_mrl];
- }
+ msg_Dbg(VLCIntf, "Reveal url %s in finder", [[url path] UTF8String]);
+ [[NSWorkspace sharedWorkspace] selectFile: [url path] inFileViewerRootedAtPath: [url path]];
+ }];
- [o_mrl release];
- }
}
/* When called retrieves the selected outlineview row and plays that node or item */
[[[VLCMain sharedInstance] info] initPanel];
}
+- (void)deletionCompleted
+{
+ // retain selection before deletion
+ [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:retainedRowSelection] byExtendingSelection:NO];
+}
+
- (IBAction)deleteItem:(id)sender
{
- int i_count;
- NSIndexSet *o_selected_indexes;
- intf_thread_t * p_intf = VLCIntf;
- playlist_t * p_playlist = pl_Get(p_intf);
+ playlist_t * p_playlist = pl_Get(VLCIntf);
// check if deletion is allowed
if (![[self model] editAllowed])
return;
- o_selected_indexes = [o_outline_view selectedRowIndexes];
- i_count = [o_selected_indexes count];
+ NSIndexSet *o_selected_indexes = [o_outline_view selectedRowIndexes];
retainedRowSelection = [o_selected_indexes firstIndex];
if (retainedRowSelection == NSNotFound)
retainedRowSelection = 0;
+ [o_selected_indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
+ PLItem *o_item = [o_outline_view itemAtRow: idx];
+ if (!o_item)
+ return;
- NSUInteger indexes[i_count];
-// if (i_count == [o_outline_view numberOfRows]) {
-// PL_LOCK;
-// playlist_NodeDelete(p_playlist, [self currentPlaylistRoot], true, false);
-// PL_UNLOCK;
-// [self playlistUpdated];
-// return;
-// }
- [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
- for (int i = 0; i < i_count; i++) {
- PLItem *o_item = [o_outline_view itemAtRow: indexes[i]];
- [o_outline_view deselectRow: indexes[i]];
-
- /// TODO
-// if (p_item->i_children != -1) {
-// //is a node and not an item
-// if (playlist_Status(p_playlist) != PLAYLIST_STOPPED &&
-// [self isItem: playlist_CurrentPlayingItem(p_playlist) inNode: ((playlist_item_t *)[o_item pointerValue])
-// checkItemExistence: NO locked:YES] == YES)
-// // if current item is in selected node and is playing then stop playlist
-// playlist_Control(p_playlist, PLAYLIST_STOP, pl_Locked);
-//
-// playlist_NodeDelete(p_playlist, p_item, true, false);
-// } else
-
- playlist_DeleteFromInput(p_playlist, [o_item input], pl_Unlocked);
-// [[o_item parent] deleteChild:o_item];
-//
-// [o_outline_view reloadData];
- }
-
-// [self playlistUpdated];
+ // model deletion is done via callback
+ playlist_DeleteFromInput(p_playlist, [o_item input], pl_Unlocked);
+ }];
}
- (IBAction)sortNodeByName:(id)sender
[o_mi_recursive_expand setEnabled: b_item_sel];
[o_mi_sort_name setEnabled: b_item_sel];
[o_mi_sort_author setEnabled: b_item_sel];
+ [o_mi_dl_cover_art setEnabled: b_item_sel];
return o_ctx_menu;
}
return;
playlist_t *p_playlist = pl_Get(p_intf);
- id o_playing_item;
-
- PL_LOCK;
- o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem(p_playlist)]];
- PL_UNLOCK;
-
NSFont *fontToUse;
if (config_GetInt(VLCIntf, "macosx-large-text"))
fontToUse = [NSFont systemFontOfSize:13.];
[cell setFont: [[NSFontManager sharedFontManager] convertFont:fontToUse toNotHaveTrait:NSBoldFontMask]];
}
-- (id)playingItem
-{
- playlist_t *p_playlist = pl_Get(VLCIntf);
-
- id o_playing_item;
-
- PL_LOCK;
- o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem(p_playlist)]];
- PL_UNLOCK;
-
- return o_playing_item;
-}
-
// TODO remove method
- (NSArray *)draggedItems
{
/* allow the user to over-write the start/stop/run-time */
if (var_GetFloat(p_input_thread, "run-time") > 0 ||
var_GetFloat(p_input_thread, "start-time") > 0 ||
- var_GetFloat(p_input_thread, "stop-time") > 0) {
+ var_GetFloat(p_input_thread, "stop-time") != 0) {
return;
}
NSInteger returnValue = NSAlertErrorReturn;
if (settingValue == 0) { // ask
+ char *psz_title_name = input_item_GetTitleFbName(p_item);
+ NSString *o_title = toNSStr(psz_title_name);
+ free(psz_title_name);
+
currentResumeTimeout = 6;
NSString *o_restartButtonLabel = _NS("Restart playback");
o_restartButtonLabel = [o_restartButtonLabel stringByAppendingFormat:@" (%d)", currentResumeTimeout];
- NSAlert *theAlert = [NSAlert alertWithMessageText:_NS("Continue playback?") defaultButton:_NS("Continue") alternateButton:o_restartButtonLabel otherButton:_NS("Always continue") informativeTextWithFormat:_NS("Playback of \"%@\" will continue at %@"), [NSString stringWithUTF8String:input_item_GetTitleFbName(p_item)], [[VLCStringUtility sharedInstance] stringForTime:lastPosition.intValue]];
+ NSAlert *theAlert = [NSAlert alertWithMessageText:_NS("Continue playback?") defaultButton:_NS("Continue") alternateButton:o_restartButtonLabel otherButton:_NS("Always continue") informativeTextWithFormat:_NS("Playback of \"%@\" will continue at %@"), o_title, [[VLCStringUtility sharedInstance] stringForTime:lastPosition.intValue]];
NSTimer *timer = [NSTimer timerWithTimeInterval:1
target:self
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
- [[VLCCoreInteraction sharedInstance] pause];
returnValue = [theAlert runModal];
[timer invalidate];
- [[VLCCoreInteraction sharedInstance] playOrPause];
// restart button was pressed or timeout happened
if (returnValue == NSAlertAlternateReturn ||
}
@end
-
-
-@implementation VLCPlaylist (NSOutlineViewDataSource)
-/* return the number of children for Obj-C pointer item */ /* DONE */
-- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
-{
- int i_return = 0;
- playlist_item_t *p_item = NULL;
- playlist_t * p_playlist = pl_Get(VLCIntf);
- //assert(outlineView == o_outline_view);
-
- PL_LOCK;
- if (!item)
- p_item = p_current_root_item;
- else
- p_item = (playlist_item_t *)[item pointerValue];
-
- if (p_item)
- i_return = p_item->i_children;
- PL_UNLOCK;
-
- return i_return > 0 ? i_return : 0;
-}
-
-/* return the child at index for the Obj-C pointer item */ /* DONE */
-- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
-{
- playlist_item_t *p_return = NULL, *p_item = NULL;
- NSValue *o_value;
- playlist_t * p_playlist = pl_Get(VLCIntf);
-
- PL_LOCK;
- if (item == nil)
- p_item = p_current_root_item; /* root object */
- else
- p_item = (playlist_item_t *)[item pointerValue];
-
- if (p_item && index < p_item->i_children && index >= 0)
- p_return = p_item->pp_children[index];
- PL_UNLOCK;
-
- o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
-
- if (o_value == nil) {
- /* FIXME: Why is there a warning if that happens all the time and seems
- * to be normal? Add an assert and fix it.
- * msg_Warn(VLCIntf, "playlist item misses pointer value, adding one"); */
- o_value = [[NSValue valueWithPointer: p_return] retain];
- }
-
- [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p", [o_value pointerValue]]];
-
- return o_value;
-}
-
-/* is the item expandable */
-- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
-{
- int i_return = 0;
- playlist_t *p_playlist = pl_Get(VLCIntf);
-
- PL_LOCK;
- if (item == nil) {
- /* root object */
- if (p_current_root_item) {
- i_return = p_current_root_item->i_children;
- }
- } else {
- playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
- if (p_item)
- i_return = p_item->i_children;
- }
- PL_UNLOCK;
-
- return (i_return >= 0);
-}
-
-/* retrieve the string values for the cells */
-- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
-{
- id o_value = nil;
- char * psz_value;
- playlist_item_t *p_item;
-
- /* For error handling */
- static BOOL attempted_reload = NO;
-
- if (item == nil || ![item isKindOfClass: [NSValue class]]) {
- /* Attempt to fix the error by asking for a data redisplay
- * This might cause infinite loop, so add a small check */
- if (!attempted_reload) {
- attempted_reload = YES;
- [outlineView reloadData];
- }
- return @"error" ;
- }
-
- p_item = (playlist_item_t *)[item pointerValue];
- if (!p_item || !p_item->p_input) {
- /* Attempt to fix the error by asking for a data redisplay
- * This might cause infinite loop, so add a small check */
- if (!attempted_reload) {
- attempted_reload = YES;
- [outlineView reloadData];
- }
- return @"error";
- }
-
- attempted_reload = NO;
- NSString * o_identifier = [o_tc identifier];
-
- if ([o_identifier isEqualToString:TRACKNUM_COLUMN]) {
- psz_value = input_item_GetTrackNumber(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:TITLE_COLUMN]) {
- /* sanity check to prevent the NSString class from crashing */
- char *psz_title = input_item_GetTitleFbName(p_item->p_input);
- if (psz_title) {
- o_value = [NSString stringWithUTF8String:psz_title];
- free(psz_title);
- }
- } else if ([o_identifier isEqualToString:ARTIST_COLUMN]) {
- psz_value = input_item_GetArtist(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:@"duration"]) {
- char psz_duration[MSTRTIME_MAX_SIZE];
- mtime_t dur = input_item_GetDuration(p_item->p_input);
- if (dur != -1) {
- secstotimestr(psz_duration, dur/1000000);
- o_value = [NSString stringWithUTF8String:psz_duration];
- }
- else
- o_value = @"--:--";
- } else if ([o_identifier isEqualToString:GENRE_COLUMN]) {
- psz_value = input_item_GetGenre(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:ALBUM_COLUMN]) {
- psz_value = input_item_GetAlbum(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:DESCRIPTION_COLUMN]) {
- psz_value = input_item_GetDescription(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:DATE_COLUMN]) {
- psz_value = input_item_GetDate(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- } else if ([o_identifier isEqualToString:LANGUAGE_COLUMN]) {
- psz_value = input_item_GetLanguage(p_item->p_input);
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- }
- else if ([o_identifier isEqualToString:URI_COLUMN]) {
- psz_value = decode_URI(input_item_GetURI(p_item->p_input));
- if (psz_value) {
- o_value = [NSString stringWithUTF8String:psz_value];
- free(psz_value);
- }
- }
- else if ([o_identifier isEqualToString:FILESIZE_COLUMN]) {
- psz_value = input_item_GetURI(p_item->p_input);
- o_value = @"";
- if (psz_value) {
- NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:psz_value]];
- if ([url isFileURL]) {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:[url path]]) {
- NSError *error;
- NSDictionary *attributes = [fileManager attributesOfItemAtPath:[url path] error:&error];
- o_value = [VLCByteCountFormatter stringFromByteCount:[attributes fileSize] countStyle:NSByteCountFormatterCountStyleDecimal];
- }
- }
- free(psz_value);
- }
- }
- else if ([o_identifier isEqualToString:@"status"]) {
- if (input_item_HasErrorWhenReading(p_item->p_input)) {
- o_value = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kAlertCautionIcon)];
- [o_value setSize: NSMakeSize(16,16)];
- }
- }
-
- return o_value;
-}
-
-@end