1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videola/n dot org>
9 * Benjamin Pracht <bigben at videolab dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
111 o_outline_dict = [[NSMutableDictionary alloc] init];
117 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
119 i_current_view = VIEW_CATEGORY;
120 playlist_ViewUpdate( p_playlist, i_current_view );
122 [o_outline_view setTarget: self];
123 [o_outline_view setDelegate: self];
124 [o_outline_view setDataSource: self];
126 vlc_object_release( p_playlist );
132 [[o_tc_name headerCell] setStringValue:_NS("Name")];
133 [[o_tc_author headerCell] setStringValue:_NS("Author")];
134 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
137 - (NSOutlineView *)outlineView
139 return o_outline_view;
142 - (playlist_item_t *)selectedPlaylistItem
144 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
150 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
152 /* return the number of children for Obj-C pointer item */ /* DONE */
153 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
156 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
158 if( p_playlist == NULL )
160 if( outlineView != o_outline_view )
162 vlc_object_release( p_playlist );
169 playlist_view_t *p_view;
170 p_view = playlist_ViewFind( p_playlist, i_current_view );
171 if( p_view && p_view->p_root )
173 i_return = p_view->p_root->i_children;
175 if( i_current_view == VIEW_CATEGORY )
177 i_return--; /* remove the GENERAL item from the list */
178 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
184 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
186 i_return = p_item->i_children;
188 vlc_object_release( p_playlist );
196 /* return the child at index for the Obj-C pointer item */ /* DONE */
197 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
199 playlist_item_t *p_return = NULL;
200 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
204 if( p_playlist == NULL )
210 playlist_view_t *p_view;
211 p_view = playlist_ViewFind( p_playlist, i_current_view );
212 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
214 if( i_current_view == VIEW_CATEGORY )
216 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
218 p_return = p_playlist->p_general->pp_children[index];
220 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
222 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
228 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
229 if( p_item && index < p_item->i_children && index >= 0 )
230 p_return = p_item->pp_children[index];
234 vlc_object_release( p_playlist );
236 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
239 o_value = [[NSValue valueWithPointer: p_return] retain];
244 /* is the item expandable */
245 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
248 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
250 if( p_playlist == NULL )
256 playlist_view_t *p_view;
257 p_view = playlist_ViewFind( p_playlist, i_current_view );
258 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
260 if( i_current_view == VIEW_CATEGORY )
263 i_return += p_playlist->p_general->i_children;
268 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
270 i_return = p_item->i_children;
272 vlc_object_release( p_playlist );
280 /* retrieve the string values for the cells */
281 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
284 intf_thread_t *p_intf = VLCIntf;
285 playlist_t *p_playlist;
286 playlist_item_t *p_item;
288 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
290 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
292 if( p_playlist == NULL )
297 p_item = (playlist_item_t *)[item pointerValue];
301 vlc_object_release( p_playlist );
305 if( [[o_tc identifier] isEqualToString:@"1"] )
307 o_value = [NSString stringWithUTF8String:
308 p_item->input.psz_name];
309 if( o_value == NULL )
310 o_value = [NSString stringWithCString:
311 p_item->input.psz_name];
313 else if( [[o_tc identifier] isEqualToString:@"2"] )
316 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
318 if( psz_temp == NULL )
322 o_value = [NSString stringWithUTF8String: psz_temp];
323 if( o_value == NULL )
325 o_value = [NSString stringWithCString: psz_temp];
330 else if( [[o_tc identifier] isEqualToString:@"3"] )
332 char psz_duration[MSTRTIME_MAX_SIZE];
333 mtime_t dur = p_item->input.i_duration;
336 secstotimestr( psz_duration, dur/1000000 );
337 o_value = [NSString stringWithUTF8String: psz_duration];
341 o_value = @"-:--:--";
344 vlc_object_release( p_playlist );
351 /*****************************************************************************
352 * VLCPlaylistWizard implementation
353 *****************************************************************************/
354 @implementation VLCPlaylistWizard
356 - (IBAction)reloadOutlineView
358 /* Only reload the outlineview if the wizard window is open since this can
359 be quite long on big playlists */
360 if( [[o_outline_view window] isVisible] )
362 [o_outline_view reloadData];
368 /*****************************************************************************
369 * VLCPlaylist implementation
370 *****************************************************************************/
371 @implementation VLCPlaylist
378 o_nodes_array = [[NSMutableArray alloc] init];
379 o_items_array = [[NSMutableArray alloc] init];
386 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
388 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
393 [super awakeFromNib];
395 [o_outline_view setDoubleAction: @selector(playItem:)];
397 [o_outline_view registerForDraggedTypes:
398 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
399 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
401 /* We need to check whether _defaultTableHeaderSortImage exists, since it
402 belongs to an Apple hidden private API, and then can "disapear" at any time*/
404 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
406 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
410 o_ascendingSortingImage = nil;
413 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
415 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
419 o_descendingSortingImage = nil;
422 o_tc_sortColumn = nil;
424 for( i_index = 0; i_index < p_list->i_count; i_index++ )
427 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
429 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
431 /* create the menu entries used in the playlist menu */
432 o_lmi = [[o_mi_services submenu] addItemWithTitle:
433 [NSString stringWithUTF8String:
434 p_parser->psz_longname ? p_parser->psz_longname :
435 ( p_parser->psz_shortname ? p_parser->psz_shortname:
436 p_parser->psz_object_name)]
437 action: @selector(servicesChange:)
439 [o_lmi setTarget: self];
440 [o_lmi setRepresentedObject:
441 [NSString stringWithCString: p_parser->psz_object_name]];
442 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
443 p_parser->psz_object_name ) )
444 [o_lmi setState: NSOnState];
446 /* create the menu entries for the main menu */
447 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
448 [NSString stringWithUTF8String:
449 p_parser->psz_longname ? p_parser->psz_longname :
450 ( p_parser->psz_shortname ? p_parser->psz_shortname:
451 p_parser->psz_object_name)]
452 action: @selector(servicesChange:)
454 [o_lmi setTarget: self];
455 [o_lmi setRepresentedObject:
456 [NSString stringWithCString: p_parser->psz_object_name]];
457 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
458 p_parser->psz_object_name ) )
459 [o_lmi setState: NSOnState];
462 vlc_list_release( p_list );
463 vlc_object_release( p_playlist );
465 /* Change the simple textfield into a searchField if we can... */
467 if( MACOS_VERSION >= 10.3 )
469 NSView *o_parentview = [o_status_field superview];
470 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
471 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
472 [o_better_search_field setDelegate:self];
473 [[NSNotificationCenter defaultCenter] addObserver: self
474 selector: @selector(searchfieldChanged:)
475 name: NSControlTextDidChangeNotification
476 object: o_better_search_field];
478 [o_better_search_field setTarget:self];
479 [o_better_search_field setAction:@selector(searchItem:)];
481 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
482 [o_parentview addSubview:o_better_search_field];
483 [o_search_field setHidden:YES];
486 //[self playlistUpdated];
489 - (void)searchfieldChanged:(NSNotification *)o_notification
491 [o_search_field setStringValue:[[o_notification object] stringValue]];
498 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
499 [o_mi_play setTitle: _NS("Play")];
500 [o_mi_delete setTitle: _NS("Delete")];
501 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
502 [o_mi_selectall setTitle: _NS("Select All")];
503 [o_mi_info setTitle: _NS("Properties")];
504 [o_mi_preparse setTitle: _NS("Preparse")];
505 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
506 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
507 [o_mi_services setTitle: _NS("Services discovery")];
508 [o_status_field setStringValue: [NSString stringWithFormat:
509 _NS("no items in playlist")]];
511 [o_random_ckb setTitle: _NS("Random")];
513 [o_search_button setTitle: _NS("Search")];
515 [o_search_field setToolTip: _NS("Search in Playlist")];
516 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
517 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
518 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
521 - (void)playlistUpdated
525 /* Clear indications of any existing column sorting*/
526 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
528 [o_outline_view setIndicatorImage:nil inTableColumn:
529 [[o_outline_view tableColumns] objectAtIndex:i]];
532 [o_outline_view setHighlightedTableColumn:nil];
533 o_tc_sortColumn = nil;
534 // TODO Find a way to keep the dict size to a minimum
535 //[o_outline_dict removeAllObjects];
536 [o_outline_view reloadData];
537 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
538 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
541 - (void)playModeUpdated
543 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
545 vlc_value_t val, val2;
547 if( p_playlist == NULL )
552 var_Get( p_playlist, "loop", &val2 );
553 var_Get( p_playlist, "repeat", &val );
554 if( val.b_bool == VLC_TRUE )
556 [o_loop_popup selectItemAtIndex: 1];
558 else if( val2.b_bool == VLC_TRUE )
560 [o_loop_popup selectItemAtIndex: 2];
564 [o_loop_popup selectItemAtIndex: 0];
567 var_Get( p_playlist, "random", &val );
568 [o_random_ckb setState: val.b_bool];
570 vlc_object_release( p_playlist );
573 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
576 for( i = 0 ; i < p_item->i_parents; i++ )
578 if( p_item->pp_parents[i]->i_view == i_current_view )
580 return p_item->pp_parents[i]->p_parent;
586 - (void)updateRowSelection
592 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
594 playlist_item_t *p_item, *p_temp_item;
595 NSMutableArray *o_array = [NSMutableArray array];
597 if( p_playlist == NULL )
600 p_item = p_playlist->status.p_item;
603 vlc_object_release(p_playlist);
607 p_temp_item = p_item;
608 while( p_temp_item->i_parents > 0 )
610 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
612 p_temp_item = [self parentOfItem: p_temp_item];
613 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
615 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
617 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
623 for (j = 0 ; j < [o_array count] - 1 ; j++)
626 if( ( o_item = [o_outline_dict objectForKey:
627 [NSString stringWithFormat: @"%p",
628 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
629 [o_outline_view expandItem: o_item];
633 i_row = [o_outline_view rowForItem:[o_outline_dict
634 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
636 [o_outline_view selectRow: i_row byExtendingSelection: NO];
637 [o_outline_view scrollRowToVisible: i_row];
639 vlc_object_release(p_playlist);
642 /* Check if p_item is a child of p_node recursively. We need to check the item
643 existence first since OSX sometimes tries to redraw items that have been
644 deleted. We don't do it when not required since this verification takes
645 quite a long time on big playlists (yes, pretty hacky). */
646 - (BOOL)isItem: (playlist_item_t *)p_item
647 inNode: (playlist_item_t *)p_node
648 checkItemExistence:(BOOL)b_check
651 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
653 playlist_item_t *p_temp_item = p_item;
655 if( p_playlist == NULL )
660 if( p_node == p_item )
662 vlc_object_release(p_playlist);
666 if( p_node->i_children < 1)
668 vlc_object_release(p_playlist);
675 vlc_mutex_lock( &p_playlist->object_lock );
679 /* Since outlineView: willDisplayCell:... may call this function with
680 p_items that don't exist anymore, first check if the item is still
681 in the playlist. Any cleaner solution welcomed. */
682 for( i = 0; i < p_playlist->i_all_size; i++ )
684 if( p_playlist->pp_all_items[i] == p_item ) break;
685 else if ( i == p_playlist->i_all_size - 1 )
687 vlc_object_release( p_playlist );
688 vlc_mutex_unlock( &p_playlist->object_lock );
694 while( p_temp_item->i_parents > 0 )
696 p_temp_item = [self parentOfItem: p_temp_item];
697 if( p_temp_item == p_node )
699 vlc_mutex_unlock( &p_playlist->object_lock );
700 vlc_object_release( p_playlist );
704 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
706 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
708 if( p_temp_item->pp_parents[i]->p_parent == p_node )
710 vlc_mutex_unlock( &p_playlist->object_lock );
711 vlc_object_release( p_playlist );
716 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
722 vlc_mutex_unlock( &p_playlist->object_lock );
725 vlc_object_release( p_playlist );
729 /* This method is usefull for instance to remove the selected children of an
730 already selected node */
731 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
734 for( i = 0 ; i < [o_items count] ; i++ )
736 for ( j = 0 ; j < [o_nodes count] ; j++ )
738 if( o_items == o_nodes)
740 if( j == i ) continue;
742 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
743 inNode: [[o_nodes objectAtIndex:j] pointerValue]
744 checkItemExistence: NO] )
746 [o_items removeObjectAtIndex:i];
747 /* We need to execute the next iteration with the same index
748 since the current item has been deleted */
757 - (IBAction)savePlaylist:(id)sender
759 intf_thread_t * p_intf = VLCIntf;
760 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
763 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
764 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
765 [o_save_panel setTitle: _NS("Save Playlist")];
766 [o_save_panel setPrompt: _NS("Save")];
768 if( [o_save_panel runModalForDirectory: nil
769 file: o_name] == NSOKButton )
771 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
773 vlc_object_release( p_playlist );
777 /* When called retrieves the selected outlineview row and plays that node or item */
778 - (IBAction)playItem:(id)sender
780 intf_thread_t * p_intf = VLCIntf;
781 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
784 if( p_playlist != NULL )
786 playlist_item_t *p_item;
787 playlist_item_t *p_node = NULL;
790 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
794 if( p_item->i_children == -1 )
796 p_node = [self parentOfItem: p_item];
798 /* for( i = 0 ; i < p_item->i_parents ; i++ )
800 if( p_item->pp_parents[i]->i_view == i_current_view )
802 p_node = p_item->pp_parents[i]->p_parent;
809 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
811 p_item = p_node->pp_children[0];
818 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
820 vlc_object_release( p_playlist );
824 /* When called retrieves the selected outlineview row and plays that node or item */
825 - (IBAction)preparseItem:(id)sender
828 NSMutableArray *o_to_preparse;
829 intf_thread_t * p_intf = VLCIntf;
830 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
833 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
834 i_count = [o_to_preparse count];
836 if( p_playlist != NULL )
840 playlist_item_t *p_item = NULL;
842 for( i = 0; i < i_count; i++ )
844 o_number = [o_to_preparse lastObject];
845 i_row = [o_number intValue];
846 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
847 [o_to_preparse removeObject: o_number];
848 [o_outline_view deselectRow: i_row];
852 if( p_item->i_children == -1 )
854 playlist_PreparseEnqueue( p_playlist, &p_item->input );
858 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
862 vlc_object_release( p_playlist );
864 [self playlistUpdated];
867 - (IBAction)servicesChange:(id)sender
869 NSMenuItem *o_mi = (NSMenuItem *)sender;
870 NSString *o_string = [o_mi representedObject];
871 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
873 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
874 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
876 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
878 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
879 [o_string cString] ) ? YES : NO];
881 i_current_view = VIEW_CATEGORY;
882 playlist_ViewUpdate( p_playlist, i_current_view );
883 vlc_object_release( p_playlist );
884 [self playlistUpdated];
888 - (IBAction)selectAll:(id)sender
890 [o_outline_view selectAll: nil];
893 - (IBAction)deleteItem:(id)sender
895 int i, i_count, i_row;
896 NSMutableArray *o_to_delete;
899 playlist_t * p_playlist;
900 intf_thread_t * p_intf = VLCIntf;
902 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
905 if ( p_playlist == NULL )
909 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
910 i_count = [o_to_delete count];
912 for( i = 0; i < i_count; i++ )
914 o_number = [o_to_delete lastObject];
915 i_row = [o_number intValue];
916 id o_item = [o_outline_view itemAtRow: i_row];
917 playlist_item_t *p_item = [o_item pointerValue];
918 [o_to_delete removeObject: o_number];
919 [o_outline_view deselectRow: i_row];
921 if( [[o_outline_view dataSource] outlineView:o_outline_view
922 numberOfChildrenOfItem: o_item] > 0 )
923 //is a node and not an item
925 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
926 [self isItem: p_playlist->status.p_item inNode:
927 ((playlist_item_t *)[o_item pointerValue])
928 checkItemExistence: NO] == YES )
930 // if current item is in selected node and is playing then stop playlist
931 playlist_Stop( p_playlist );
933 vlc_mutex_lock( &p_playlist->object_lock );
934 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
935 vlc_mutex_unlock( &p_playlist->object_lock );
939 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
940 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
942 playlist_Stop( p_playlist );
944 vlc_mutex_lock( &p_playlist->object_lock );
945 playlist_Delete( p_playlist, p_item->input.i_id );
946 vlc_mutex_unlock( &p_playlist->object_lock );
949 [self playlistUpdated];
950 vlc_object_release( p_playlist );
953 - (IBAction)sortNodeByName:(id)sender
955 [self sortNode: SORT_TITLE];
958 - (IBAction)sortNodeByAuthor:(id)sender
960 [self sortNode: SORT_AUTHOR];
963 - (void)sortNode:(int)i_mode
965 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
967 playlist_item_t * p_item;
969 if (p_playlist == NULL)
974 if( [o_outline_view selectedRow] > -1 )
976 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
980 /*If no item is selected, sort the whole playlist*/
982 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
983 p_item = p_view->p_root;
986 if( p_item->i_children > -1 ) // the item is a node
988 vlc_mutex_lock( &p_playlist->object_lock );
989 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
990 vlc_mutex_unlock( &p_playlist->object_lock );
996 for( i = 0 ; i < p_item->i_parents ; i++ )
998 if( p_item->pp_parents[i]->i_view == i_current_view )
1000 vlc_mutex_lock( &p_playlist->object_lock );
1001 playlist_RecursiveNodeSort( p_playlist,
1002 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
1003 vlc_mutex_unlock( &p_playlist->object_lock );
1008 vlc_object_release( p_playlist );
1009 [self playlistUpdated];
1012 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
1014 intf_thread_t * p_intf = VLCIntf;
1015 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1018 if( p_playlist == NULL )
1022 playlist_item_t *p_item;
1024 BOOL b_rem = FALSE, b_dir = FALSE;
1025 NSString *o_uri, *o_name;
1030 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1031 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1032 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1034 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1035 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1037 int i_count, i_index;
1038 struct statfs *mounts = NULL;
1040 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1041 /* getmntinfo returns a pointer to static data. Do not free. */
1042 for( i_index = 0 ; i_index < i_count; i_index++ )
1044 NSMutableString *o_temp, *o_temp2;
1045 o_temp = [NSMutableString stringWithString: o_uri];
1046 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1047 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1048 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1049 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1051 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1053 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1057 /* If no name, then make a guess */
1058 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1060 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1061 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1062 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1064 /* All of this is to make sure CD's play when you D&D them on VLC */
1065 /* Converts mountpoint to a /dev file */
1068 NSMutableString *o_temp;
1070 buf = (struct statfs *) malloc (sizeof(struct statfs));
1071 statfs( [o_uri fileSystemRepresentation], buf );
1072 psz_dev = strdup(buf->f_mntfromname);
1073 o_temp = [NSMutableString stringWithCString: psz_dev ];
1074 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1075 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1076 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1080 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1086 for( i = 0; i < (int)[o_options count]; i++ )
1088 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1092 /* Recent documents menu */
1093 o_true_file = [NSURL fileURLWithPath: o_uri];
1094 if( o_true_file != nil )
1096 [[NSDocumentController sharedDocumentController]
1097 noteNewRecentDocumentURL: o_true_file];
1100 vlc_object_release( p_playlist );
1104 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1107 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1109 if( p_playlist == NULL )
1114 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1116 playlist_item_t *p_item;
1117 NSDictionary *o_one_item;
1120 o_one_item = [o_array objectAtIndex: i_item];
1121 p_item = [self createItem: o_one_item];
1128 playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1130 if( i_item == 0 && !b_enqueue )
1132 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1135 vlc_object_release( p_playlist );
1138 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1141 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1143 if( p_playlist == NULL )
1148 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1150 playlist_item_t *p_item;
1151 NSDictionary *o_one_item;
1154 o_one_item = [o_array objectAtIndex: i_item];
1155 p_item = [self createItem: o_one_item];
1162 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
1164 if( i_item == 0 && !b_enqueue )
1166 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1169 vlc_object_release( p_playlist );
1173 - (IBAction)handlePopUp:(id)sender
1176 intf_thread_t * p_intf = VLCIntf;
1177 vlc_value_t val1,val2;
1178 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1180 if( p_playlist == NULL )
1185 switch( [o_loop_popup indexOfSelectedItem] )
1190 var_Set( p_playlist, "loop", val1 );
1192 var_Set( p_playlist, "repeat", val1 );
1193 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1198 var_Set( p_playlist, "repeat", val1 );
1200 var_Set( p_playlist, "loop", val1 );
1201 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1205 var_Get( p_playlist, "repeat", &val1 );
1206 var_Get( p_playlist, "loop", &val2 );
1207 if( val1.b_bool || val2.b_bool )
1210 var_Set( p_playlist, "repeat", val1 );
1211 var_Set( p_playlist, "loop", val1 );
1212 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1216 vlc_object_release( p_playlist );
1217 [self playlistUpdated];
1220 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1222 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1224 playlist_item_t *p_selected_item;
1225 int i_current, i_selected_row;
1230 i_selected_row = [o_outline_view selectedRow];
1231 if (i_selected_row < 0)
1234 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1235 i_selected_row] pointerValue];
1237 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1240 NSString *o_current_name, *o_current_author;
1242 vlc_mutex_lock( &p_playlist->object_lock );
1243 o_current_name = [NSString stringWithUTF8String:
1244 p_item->pp_children[i_current]->input.psz_name];
1245 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1246 _("Meta-information"),_("Artist") );
1247 o_current_author = [NSString stringWithUTF8String: psz_temp];
1249 vlc_mutex_unlock( &p_playlist->object_lock );
1251 if( p_selected_item == p_item->pp_children[i_current] &&
1252 b_selected_item_met == NO )
1254 b_selected_item_met = YES;
1256 else if( p_selected_item == p_item->pp_children[i_current] &&
1257 b_selected_item_met == YES )
1259 vlc_object_release( p_playlist );
1262 else if( b_selected_item_met == YES &&
1263 ( [o_current_name rangeOfString:[o_search_field
1264 stringValue] options:NSCaseInsensitiveSearch ].length ||
1265 [o_current_author rangeOfString:[o_search_field
1266 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1268 vlc_object_release( p_playlist );
1269 /*Adds the parent items in the result array as well, so that we can
1271 return [NSMutableArray arrayWithObject: [NSValue
1272 valueWithPointer: p_item->pp_children[i_current]]];
1274 if( p_item->pp_children[i_current]->i_children > 0 )
1276 id o_result = [self subSearchItem:
1277 p_item->pp_children[i_current]];
1278 if( o_result != NULL )
1280 vlc_object_release( p_playlist );
1281 [o_result insertObject: [NSValue valueWithPointer:
1282 p_item->pp_children[i_current]] atIndex:0];
1287 vlc_object_release( p_playlist );
1291 - (IBAction)searchItem:(id)sender
1293 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1295 playlist_view_t * p_view;
1301 b_selected_item_met = NO;
1303 if( p_playlist == NULL )
1305 p_view = playlist_ViewFind( p_playlist, i_current_view );
1309 /*First, only search after the selected item:*
1310 *(b_selected_item_met = NO) */
1311 o_result = [self subSearchItem:p_view->p_root];
1312 if( o_result == NULL )
1314 /* If the first search failed, search again from the beginning */
1315 o_result = [self subSearchItem:p_view->p_root];
1317 if( o_result != NULL )
1320 if( [[o_result objectAtIndex: 0] pointerValue] ==
1321 p_playlist->p_general )
1326 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1328 [o_outline_view expandItem: [o_outline_dict objectForKey:
1329 [NSString stringWithFormat: @"%p",
1330 [[o_result objectAtIndex: i] pointerValue]]]];
1332 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1333 [NSString stringWithFormat: @"%p",
1334 [[o_result objectAtIndex: [o_result count] - 1 ]
1339 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1340 [o_outline_view scrollRowToVisible: i_row];
1343 vlc_object_release( p_playlist );
1346 - (IBAction)recursiveExpandNode:(id)sender
1349 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1350 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1352 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1353 isItemExpandable: o_item] )
1355 for( i = 0 ; i < p_item->i_parents ; i++ )
1357 if( p_item->pp_parents[i]->i_view == i_current_view )
1359 o_item = [o_outline_dict objectForKey: [NSString
1360 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1366 /* We need to collapse the node first, since OSX refuses to recursively
1367 expand an already expanded node, even if children nodes are collapsed. */
1368 [o_outline_view collapseItem: o_item collapseChildren: YES];
1369 [o_outline_view expandItem: o_item expandChildren: YES];
1372 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1376 vlc_bool_t b_item_sel;
1378 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1380 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1381 [o_outline_view selectedRow] != -1 );
1382 b_rows = [o_outline_view numberOfRows] != 0;
1384 [o_mi_play setEnabled: b_item_sel];
1385 [o_mi_delete setEnabled: b_item_sel];
1386 [o_mi_selectall setEnabled: b_rows];
1387 [o_mi_info setEnabled: b_item_sel];
1388 [o_mi_preparse setEnabled: b_item_sel];
1389 [o_mi_recursive_expand setEnabled: b_item_sel];
1390 [o_mi_sort_name setEnabled: b_item_sel];
1391 [o_mi_sort_author setEnabled: b_item_sel];
1393 return( o_ctx_menu );
1396 - (void)outlineView: (NSTableView*)o_tv
1397 didClickTableColumn:(NSTableColumn *)o_tc
1399 int i_mode = 0, i_type;
1400 intf_thread_t *p_intf = VLCIntf;
1401 playlist_view_t *p_view;
1403 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1405 if( p_playlist == NULL )
1410 /* Check whether the selected table column header corresponds to a
1411 sortable table column*/
1412 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1414 vlc_object_release( p_playlist );
1418 p_view = playlist_ViewFind( p_playlist, i_current_view );
1420 if( o_tc_sortColumn == o_tc )
1422 b_isSortDescending = !b_isSortDescending;
1426 b_isSortDescending = VLC_FALSE;
1429 if( o_tc == o_tc_name )
1431 i_mode = SORT_TITLE;
1433 else if( o_tc == o_tc_author )
1435 i_mode = SORT_AUTHOR;
1438 if( b_isSortDescending )
1440 i_type = ORDER_REVERSE;
1444 i_type = ORDER_NORMAL;
1447 vlc_mutex_lock( &p_playlist->object_lock );
1448 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1449 vlc_mutex_unlock( &p_playlist->object_lock );
1451 vlc_object_release( p_playlist );
1452 [self playlistUpdated];
1454 o_tc_sortColumn = o_tc;
1455 [o_outline_view setHighlightedTableColumn:o_tc];
1457 if( b_isSortDescending )
1459 [o_outline_view setIndicatorImage:o_descendingSortingImage
1460 inTableColumn:o_tc];
1464 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1465 inTableColumn:o_tc];
1470 - (void)outlineView:(NSOutlineView *)outlineView
1471 willDisplayCell:(id)cell
1472 forTableColumn:(NSTableColumn *)tableColumn
1475 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1480 if( !p_playlist ) return;
1482 o_playing_item = [o_outline_dict objectForKey:
1483 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1485 if( [self isItem: [o_playing_item pointerValue] inNode:
1486 [item pointerValue] checkItemExistence: YES]
1487 || [o_playing_item isEqual: item] )
1489 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1493 [cell setFont: [NSFont systemFontOfSize: 0]];
1495 vlc_object_release( p_playlist );
1500 @implementation VLCPlaylist (NSOutlineViewDataSource)
1502 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1504 id o_value = [super outlineView: outlineView child: index ofItem: item];
1505 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1508 if( !p_playlist ) return nil;
1510 if( p_playlist->i_size >= 2 )
1512 [o_status_field setStringValue: [NSString stringWithFormat:
1513 _NS("%i items in playlist"), p_playlist->i_size]];
1517 if( p_playlist->i_size == 0 )
1519 [o_status_field setStringValue: [NSString stringWithFormat:
1520 _NS("no items in playlist"), p_playlist->i_size]];
1524 [o_status_field setStringValue: [NSString stringWithFormat:
1525 _NS("1 item in playlist"), p_playlist->i_size]];
1528 vlc_object_release( p_playlist );
1530 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1531 [o_value pointerValue]]];
1537 /* Required for drag & drop and reordering */
1538 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1541 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1544 /* First remove the items that were moved during the last drag & drop
1546 [o_items_array removeAllObjects];
1547 [o_nodes_array removeAllObjects];
1549 if( !p_playlist ) return NO;
1551 for( i = 0 ; i < [items count] ; i++ )
1553 id o_item = [items objectAtIndex: i];
1555 /* Refuse to move items that are not in the General Node
1556 (Service Discovery) */
1557 if( ![self isItem: [o_item pointerValue] inNode:
1558 p_playlist->p_general checkItemExistence: NO])
1560 vlc_object_release(p_playlist);
1563 /* Fill the items and nodes to move in 2 different arrays */
1564 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1565 [o_nodes_array addObject: o_item];
1567 [o_items_array addObject: o_item];
1570 /* Now we need to check if there are selected items that are in already
1571 selected nodes. In that case, we only want to move the nodes */
1572 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1573 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1578 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1580 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1582 if( j == i ) continue;
1583 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1584 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1586 [o_nodes_array removeObjectAtIndex:i];
1587 /* We need to execute the next iteration with the same index
1588 since the current item has been deleted */
1595 for( i = 0 ; i < [o_items_array count] ; i++ )
1597 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1599 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1600 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1602 [o_items_array removeObjectAtIndex:i];
1609 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1610 a Drop operation com�ng from the playlist.
1611 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1614 [pboard declareTypes: [NSArray arrayWithObjects:
1615 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1616 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1617 [pboard setPropertyList:[NSArray array]
1618 forType:NSFilenamesPboardType];
1620 vlc_object_release(p_playlist);
1624 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1626 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1628 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1630 if( !p_playlist ) return NSDragOperationNone;
1632 /* Dropping ON items is not allowed */
1633 if( index == NSOutlineViewDropOnItemIndex )
1635 vlc_object_release( p_playlist );
1636 return NSDragOperationNone;
1639 /* We refuse to drop an item in anything else than a child of the General
1640 Node. We still accept items that would be root nodes of the outlineview
1641 however, to allow drop in an empty playlist. */
1642 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1643 checkItemExistence: NO] || item == nil) )
1645 vlc_object_release( p_playlist );
1646 return NSDragOperationNone;
1649 /* Drop from the Playlist */
1650 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1653 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1655 /* We refuse to Drop in a child of an item we are moving */
1656 if( [self isItem: [item pointerValue] inNode:
1657 [[o_nodes_array objectAtIndex: i] pointerValue]
1658 checkItemExistence: NO] )
1660 vlc_object_release( p_playlist );
1661 return NSDragOperationNone;
1664 vlc_object_release(p_playlist);
1665 return NSDragOperationMove;
1668 /* Drop from the Finder */
1669 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1671 vlc_object_release(p_playlist);
1672 return NSDragOperationGeneric;
1674 vlc_object_release(p_playlist);
1675 return NSDragOperationNone;
1678 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1680 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1682 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1684 if( !p_playlist ) return NO;
1686 /* Drag & Drop inside the playlist */
1687 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1691 playlist_item_t *p_new_parent, *p_item = NULL;
1692 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1694 /* If the item is to be dropped as root item of the outline, make it a
1695 child of the General node.
1696 Else, choose the proposed parent as parent. */
1698 p_new_parent = p_playlist->p_general;
1700 p_new_parent = [item pointerValue];
1702 /* If the proposed parent is not a node, then use the parent node of
1704 if( p_new_parent->i_children <= 0 )
1707 playlist_item_t *p_temp_item = p_new_parent;
1708 p_new_parent = [self parentOfItem: p_new_parent];
1711 vlc_object_release(p_playlist);
1714 /* Calculate the position of the dropped item in this new parent:
1715 following the first proposed parent. */
1716 for( j = 0; j < p_new_parent->i_children; j++ )
1718 if( p_new_parent->pp_children[j] == p_temp_item )
1723 else if( j == p_new_parent->i_children - 1 )
1728 for( i = 0; i < [o_all_items count]; i++ )
1730 playlist_item_t *p_old_parent = NULL;
1731 int i_old_index = 0;
1733 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1734 p_old_parent = [self parentOfItem: p_item];
1737 /* We may need the old index later */
1738 if( p_new_parent == p_old_parent )
1741 for( j = 0; j < p_old_parent->i_children; j++ )
1743 if( p_old_parent->pp_children[j] == p_item )
1752 /* If we move the playing item in a different node or we move the
1753 node containing the playing item in a different node, then stop
1754 playback, or the playlist refuses to detach the item. */
1755 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1756 (( p_item == p_playlist->status.p_item &&
1757 p_new_parent != p_old_parent) ||
1758 ( p_item->i_children > 0 &&
1759 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1761 playlist_Stop( p_playlist );
1763 vlc_mutex_lock( &p_playlist->object_lock );
1764 // Acually detach the item from the old position
1765 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1767 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1771 /* Calculate the new index */
1774 /* If we move the item in the same node, we need to take into
1775 account that one item will be deleted */
1776 else if((p_new_parent == p_old_parent &&
1777 i_old_index < index + (int)i)
1778 || p_new_parent == p_playlist->p_general || index == 0 )
1779 i_new_index = index + i;
1781 i_new_index = index + i + 1;
1782 // Reattach the item to the new position
1783 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1784 p_new_parent, i_new_index );
1786 vlc_mutex_unlock( &p_playlist->object_lock );
1788 [self playlistUpdated];
1789 i_row = [o_outline_view rowForItem:[o_outline_dict
1790 objectForKey:[NSString stringWithFormat: @"%p",
1791 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1795 i_row = [o_outline_view rowForItem:[o_outline_dict
1796 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1799 [o_outline_view deselectAll: self];
1800 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1801 [o_outline_view scrollRowToVisible: i_row];
1803 vlc_object_release(p_playlist);
1807 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1810 playlist_item_t *p_node = [item pointerValue];
1812 NSArray *o_array = [NSArray array];
1813 NSArray *o_values = [[o_pasteboard propertyListForType:
1814 NSFilenamesPboardType]
1815 sortedArrayUsingSelector:
1816 @selector(caseInsensitiveCompare:)];
1818 for( i = 0; i < (int)[o_values count]; i++)
1820 NSDictionary *o_dic;
1821 o_dic = [NSDictionary dictionaryWithObject:[o_values
1822 objectAtIndex:i] forKey:@"ITEM_URL"];
1823 o_array = [o_array arrayByAddingObject: o_dic];
1828 [self appendArray: o_array atPos: index enqueue: YES];
1830 else if( p_node->i_children == -1 )
1833 playlist_item_t *p_real_node = NULL;
1835 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1837 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1839 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1842 if( i_counter == p_node->i_parents )
1844 vlc_object_release(p_playlist);
1848 [self appendNodeArray: o_array inNode: p_real_node
1849 atPos: index inView: i_current_view enqueue: YES];
1853 [self appendNodeArray: o_array inNode: p_node
1854 atPos: index inView: i_current_view enqueue: YES];
1856 vlc_object_release( p_playlist );
1859 vlc_object_release( p_playlist );
1863 /* Delegate method of NSWindow */
1864 /*- (void)windowWillClose:(NSNotification *)aNotification
1866 [o_btn_playlist setState: NSOffState];