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
108 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
110 i_current_view = VIEW_CATEGORY;
111 playlist_ViewUpdate( p_playlist, i_current_view );
113 [o_outline_view setTarget: self];
114 [o_outline_view setDelegate: self];
115 [o_outline_view setDataSource: self];
117 vlc_object_release( p_playlist );
123 [[o_tc_name headerCell] setStringValue:_NS("Name")];
124 [[o_tc_author headerCell] setStringValue:_NS("Author")];
125 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
128 - (NSOutlineView *)outlineView
130 return o_outline_view;
133 - (playlist_item_t *)selectedPlaylistItem
135 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
141 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
143 /* return the number of children for Obj-C pointer item */ /* DONE */
144 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
147 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
149 if( p_playlist == NULL )
151 if( outlineView != o_outline_view )
153 vlc_object_release( p_playlist );
160 playlist_view_t *p_view;
161 p_view = playlist_ViewFind( p_playlist, i_current_view );
162 if( p_view && p_view->p_root )
164 i_return = p_view->p_root->i_children;
165 if( i_current_view == VIEW_CATEGORY )
167 i_return--; /* remove the GENERAL item from the list */
168 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
174 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
176 i_return = p_item->i_children;
178 vlc_object_release( p_playlist );
186 /* return the child at index for the Obj-C pointer item */ /* DONE */
187 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
189 playlist_item_t *p_return = NULL;
190 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
194 if( p_playlist == NULL )
200 playlist_view_t *p_view;
201 p_view = playlist_ViewFind( p_playlist, i_current_view );
202 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
204 if( i_current_view == VIEW_CATEGORY )
206 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
208 p_return = p_playlist->p_general->pp_children[index];
210 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
212 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
218 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
219 if( p_item && index < p_item->i_children && index >= 0 )
220 p_return = p_item->pp_children[index];
224 vlc_object_release( p_playlist );
226 o_value = [[NSValue valueWithPointer: p_return] retain];
231 /* is the item expandable */
232 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
235 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
237 if( p_playlist == NULL )
243 playlist_view_t *p_view;
244 p_view = playlist_ViewFind( p_playlist, i_current_view );
245 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
247 if( i_current_view == VIEW_CATEGORY )
250 i_return += p_playlist->p_general->i_children;
255 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
257 i_return = p_item->i_children;
259 vlc_object_release( p_playlist );
267 /* retrieve the string values for the cells */
268 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
271 intf_thread_t *p_intf = VLCIntf;
272 playlist_t *p_playlist;
273 playlist_item_t *p_item;
275 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
277 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
279 if( p_playlist == NULL )
284 p_item = (playlist_item_t *)[item pointerValue];
288 vlc_object_release( p_playlist );
292 if( [[o_tc identifier] isEqualToString:@"1"] )
294 o_value = [NSString stringWithUTF8String:
295 p_item->input.psz_name];
296 if( o_value == NULL )
297 o_value = [NSString stringWithCString:
298 p_item->input.psz_name];
300 else if( [[o_tc identifier] isEqualToString:@"2"] )
303 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
305 if( psz_temp == NULL )
309 o_value = [NSString stringWithUTF8String: psz_temp];
310 if( o_value == NULL )
312 o_value = [NSString stringWithCString: psz_temp];
317 else if( [[o_tc identifier] isEqualToString:@"3"] )
319 char psz_duration[MSTRTIME_MAX_SIZE];
320 mtime_t dur = p_item->input.i_duration;
323 secstotimestr( psz_duration, dur/1000000 );
324 o_value = [NSString stringWithUTF8String: psz_duration];
328 o_value = @"-:--:--";
331 vlc_object_release( p_playlist );
338 /*****************************************************************************
339 * VLCPlaylistWizard implementation
340 *****************************************************************************/
341 @implementation VLCPlaylistWizard
343 - (IBAction)reloadOutlineView
345 /* Only reload the outlineview if the wizard window is open since this can
346 be quite long on big playlists */
347 if( [[o_outline_view window] isVisible] )
349 [o_outline_view reloadData];
355 /*****************************************************************************
356 * VLCPlaylist implementation
357 *****************************************************************************/
358 @implementation VLCPlaylist
365 o_outline_dict = [[NSMutableDictionary alloc] init];
366 o_nodes_array = [[NSMutableArray alloc] init];
367 o_items_array = [[NSMutableArray alloc] init];
377 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
379 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
384 [super awakeFromNib];
386 [o_outline_view setDoubleAction: @selector(playItem:)];
388 [o_outline_view registerForDraggedTypes:
389 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
390 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
392 /* We need to check whether _defaultTableHeaderSortImage exists, since it
393 belongs to an Apple hidden private API, and then can "disapear" at any time*/
395 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
397 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
401 o_ascendingSortingImage = nil;
404 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
406 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
410 o_descendingSortingImage = nil;
413 o_tc_sortColumn = nil;
415 for( i_index = 0; i_index < p_list->i_count; i_index++ )
418 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
420 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
422 /* create the menu entries used in the playlist menu */
423 o_lmi = [[o_mi_services submenu] addItemWithTitle:
424 [NSString stringWithUTF8String:
425 p_parser->psz_longname ? p_parser->psz_longname :
426 ( p_parser->psz_shortname ? p_parser->psz_shortname:
427 p_parser->psz_object_name)]
428 action: @selector(servicesChange:)
430 [o_lmi setTarget: self];
431 [o_lmi setRepresentedObject:
432 [NSString stringWithCString: p_parser->psz_object_name]];
433 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
434 p_parser->psz_object_name ) )
435 [o_lmi setState: NSOnState];
437 /* create the menu entries for the main menu */
438 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
439 [NSString stringWithUTF8String:
440 p_parser->psz_longname ? p_parser->psz_longname :
441 ( p_parser->psz_shortname ? p_parser->psz_shortname:
442 p_parser->psz_object_name)]
443 action: @selector(servicesChange:)
445 [o_lmi setTarget: self];
446 [o_lmi setRepresentedObject:
447 [NSString stringWithCString: p_parser->psz_object_name]];
448 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
449 p_parser->psz_object_name ) )
450 [o_lmi setState: NSOnState];
453 vlc_list_release( p_list );
454 vlc_object_release( p_playlist );
456 /* Change the simple textfield into a searchField if we can... */
458 if( MACOS_VERSION >= 10.3 )
460 NSView *o_parentview = [o_status_field superview];
461 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
462 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
463 [o_better_search_field setDelegate:self];
464 [[NSNotificationCenter defaultCenter] addObserver: self
465 selector: @selector(searchfieldChanged:)
466 name: NSControlTextDidChangeNotification
467 object: o_better_search_field];
469 [o_better_search_field setTarget:self];
470 [o_better_search_field setAction:@selector(searchItem:)];
472 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
473 [o_parentview addSubview:o_better_search_field];
474 [o_search_field setHidden:YES];
477 //[self playlistUpdated];
480 - (void)searchfieldChanged:(NSNotification *)o_notification
482 [o_search_field setStringValue:[[o_notification object] stringValue]];
489 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
490 [o_mi_play setTitle: _NS("Play")];
491 [o_mi_delete setTitle: _NS("Delete")];
492 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
493 [o_mi_selectall setTitle: _NS("Select All")];
494 [o_mi_info setTitle: _NS("Properties")];
495 [o_mi_preparse setTitle: _NS("Preparse")];
496 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
497 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
498 [o_mi_services setTitle: _NS("Services discovery")];
499 [o_status_field setStringValue: [NSString stringWithFormat:
500 _NS("no items in playlist")]];
502 [o_random_ckb setTitle: _NS("Random")];
504 [o_search_button setTitle: _NS("Search")];
506 [o_search_field setToolTip: _NS("Search in Playlist")];
507 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
508 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
509 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
512 - (void)playlistUpdated
516 /* Clear indications of any existing column sorting*/
517 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
519 [o_outline_view setIndicatorImage:nil inTableColumn:
520 [[o_outline_view tableColumns] objectAtIndex:i]];
523 [o_outline_view setHighlightedTableColumn:nil];
524 o_tc_sortColumn = nil;
525 // TODO Find a way to keep the dict size to a minimum
526 //[o_outline_dict removeAllObjects];
527 [o_outline_view reloadData];
528 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
529 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
532 - (void)playModeUpdated
534 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
536 vlc_value_t val, val2;
538 if( p_playlist == NULL )
543 var_Get( p_playlist, "loop", &val2 );
544 var_Get( p_playlist, "repeat", &val );
545 if( val.b_bool == VLC_TRUE )
547 [o_loop_popup selectItemAtIndex: 1];
549 else if( val2.b_bool == VLC_TRUE )
551 [o_loop_popup selectItemAtIndex: 2];
555 [o_loop_popup selectItemAtIndex: 0];
558 var_Get( p_playlist, "random", &val );
559 [o_random_ckb setState: val.b_bool];
561 vlc_object_release( p_playlist );
564 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
567 for( i = 0 ; i < p_item->i_parents; i++ )
569 if( p_item->pp_parents[i]->i_view == i_current_view )
571 return p_item->pp_parents[i]->p_parent;
577 - (void)updateRowSelection
583 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
585 playlist_item_t *p_item, *p_temp_item;
586 NSMutableArray *o_array = [NSMutableArray array];
588 if( p_playlist == NULL )
591 p_item = p_playlist->status.p_item;
594 vlc_object_release(p_playlist);
598 p_temp_item = p_item;
599 while( p_temp_item->i_parents > 0 )
601 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
603 p_temp_item = [self parentOfItem: p_temp_item];
604 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
606 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
608 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
614 for (j = 0 ; j < [o_array count] - 1 ; j++)
617 if( ( o_item = [o_outline_dict objectForKey:
618 [NSString stringWithFormat: @"%p",
619 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
620 [o_outline_view expandItem: o_item];
624 i_row = [o_outline_view rowForItem:[o_outline_dict
625 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
627 [o_outline_view selectRow: i_row byExtendingSelection: NO];
628 [o_outline_view scrollRowToVisible: i_row];
630 vlc_object_release(p_playlist);
633 /* 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
634 deleted. We don't do it when not required since this verification takes
635 quite a long time on big playlists (yes, pretty hacky). */
636 - (BOOL)isItem: (playlist_item_t *)p_item
637 inNode: (playlist_item_t *)p_node
638 checkItemExistence:(BOOL)b_check
641 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
643 playlist_item_t *p_temp_item = p_item;
645 if( p_playlist == NULL )
650 if( p_node == p_item )
652 vlc_object_release(p_playlist);
656 if( p_node->i_children < 1)
658 vlc_object_release(p_playlist);
665 vlc_mutex_lock( &p_playlist->object_lock );
669 /* Since outlineView: willDisplayCell:... may call this function with
670 p_items that don't exist anymore, first check if the item is still
671 in the playlist. Any cleaner solution welcomed. */
672 for( i = 0; i < p_playlist->i_all_size; i++ )
674 if( p_playlist->pp_all_items[i] == p_item ) break;
675 else if ( i == p_playlist->i_all_size - 1 )
677 vlc_object_release( p_playlist );
678 vlc_mutex_unlock( &p_playlist->object_lock );
684 while( p_temp_item->i_parents > 0 )
686 p_temp_item = [self parentOfItem: p_temp_item];
687 if( p_temp_item == p_node )
689 vlc_mutex_unlock( &p_playlist->object_lock );
690 vlc_object_release( p_playlist );
694 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
696 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
698 if( p_temp_item->pp_parents[i]->p_parent == p_node )
700 vlc_mutex_unlock( &p_playlist->object_lock );
701 vlc_object_release( p_playlist );
706 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
712 vlc_mutex_unlock( &p_playlist->object_lock );
715 vlc_object_release( p_playlist );
719 /* This method is usefull for instance to remove the selected children of an
720 already selected node */
721 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
724 for( i = 0 ; i < [o_items count] ; i++ )
726 for ( j = 0 ; j < [o_nodes count] ; j++ )
728 if( o_items == o_nodes)
730 if( j == i ) continue;
732 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
733 inNode: [[o_nodes objectAtIndex:j] pointerValue]
734 checkItemExistence: NO] )
736 [o_items removeObjectAtIndex:i];
737 /* We need to execute the next iteration with the same index
738 since the current item has been deleted */
747 - (IBAction)savePlaylist:(id)sender
749 intf_thread_t * p_intf = VLCIntf;
750 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
753 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
754 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
755 [o_save_panel setTitle: _NS("Save Playlist")];
756 [o_save_panel setPrompt: _NS("Save")];
758 if( [o_save_panel runModalForDirectory: nil
759 file: o_name] == NSOKButton )
761 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
763 vlc_object_release( p_playlist );
767 /* When called retrieves the selected outlineview row and plays that node or item */
768 - (IBAction)playItem:(id)sender
770 intf_thread_t * p_intf = VLCIntf;
771 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
774 if( p_playlist != NULL )
776 playlist_item_t *p_item;
777 playlist_item_t *p_node = NULL;
780 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
784 if( p_item->i_children == -1 )
786 p_node = [self parentOfItem: p_item];
788 /* for( i = 0 ; i < p_item->i_parents ; i++ )
790 if( p_item->pp_parents[i]->i_view == i_current_view )
792 p_node = p_item->pp_parents[i]->p_parent;
799 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
801 p_item = p_node->pp_children[0];
808 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
810 vlc_object_release( p_playlist );
814 /* When called retrieves the selected outlineview row and plays that node or item */
815 - (IBAction)preparseItem:(id)sender
818 NSMutableArray *o_to_preparse;
819 intf_thread_t * p_intf = VLCIntf;
820 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
823 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
824 i_count = [o_to_preparse count];
826 if( p_playlist != NULL )
830 playlist_item_t *p_item = NULL;
832 for( i = 0; i < i_count; i++ )
834 o_number = [o_to_preparse lastObject];
835 i_row = [o_number intValue];
836 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
837 [o_to_preparse removeObject: o_number];
838 [o_outline_view deselectRow: i_row];
842 if( p_item->i_children == -1 )
844 playlist_PreparseEnqueue( p_playlist, &p_item->input );
848 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
852 vlc_object_release( p_playlist );
854 [self playlistUpdated];
857 - (IBAction)servicesChange:(id)sender
859 NSMenuItem *o_mi = (NSMenuItem *)sender;
860 NSString *o_string = [o_mi representedObject];
861 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
863 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
864 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
866 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
868 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
869 [o_string cString] ) ? YES : NO];
871 i_current_view = VIEW_CATEGORY;
872 playlist_ViewUpdate( p_playlist, i_current_view );
873 vlc_object_release( p_playlist );
874 [self playlistUpdated];
878 - (IBAction)selectAll:(id)sender
880 [o_outline_view selectAll: nil];
883 - (IBAction)deleteItem:(id)sender
885 int i, i_count, i_row;
886 NSMutableArray *o_to_delete;
889 playlist_t * p_playlist;
890 intf_thread_t * p_intf = VLCIntf;
892 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
895 if ( p_playlist == NULL )
899 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
900 i_count = [o_to_delete count];
902 for( i = 0; i < i_count; i++ )
904 o_number = [o_to_delete lastObject];
905 i_row = [o_number intValue];
906 id o_item = [o_outline_view itemAtRow: i_row];
907 playlist_item_t *p_item = [o_item pointerValue];
908 [o_to_delete removeObject: o_number];
909 [o_outline_view deselectRow: i_row];
911 if( [[o_outline_view dataSource] outlineView:o_outline_view
912 numberOfChildrenOfItem: o_item] > 0 )
913 //is a node and not an item
915 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
916 [self isItem: p_playlist->status.p_item inNode:
917 ((playlist_item_t *)[o_item pointerValue])
918 checkItemExistence: NO] == YES )
920 // if current item is in selected node and is playing then stop playlist
921 playlist_Stop( p_playlist );
923 vlc_mutex_lock( &p_playlist->object_lock );
924 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
925 vlc_mutex_unlock( &p_playlist->object_lock );
929 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
930 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
932 playlist_Stop( p_playlist );
934 vlc_mutex_lock( &p_playlist->object_lock );
935 playlist_Delete( p_playlist, p_item->input.i_id );
936 vlc_mutex_unlock( &p_playlist->object_lock );
939 [self playlistUpdated];
940 vlc_object_release( p_playlist );
943 - (IBAction)sortNodeByName:(id)sender
945 [self sortNode: SORT_TITLE];
948 - (IBAction)sortNodeByAuthor:(id)sender
950 [self sortNode: SORT_AUTHOR];
953 - (void)sortNode:(int)i_mode
955 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
957 playlist_item_t * p_item;
959 if (p_playlist == NULL)
964 if( [o_outline_view selectedRow] > -1 )
966 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
970 /*If no item is selected, sort the whole playlist*/
972 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
973 p_item = p_view->p_root;
976 if( p_item->i_children > -1 ) // the item is a node
978 vlc_mutex_lock( &p_playlist->object_lock );
979 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
980 vlc_mutex_unlock( &p_playlist->object_lock );
986 for( i = 0 ; i < p_item->i_parents ; i++ )
988 if( p_item->pp_parents[i]->i_view == i_current_view )
990 vlc_mutex_lock( &p_playlist->object_lock );
991 playlist_RecursiveNodeSort( p_playlist,
992 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
993 vlc_mutex_unlock( &p_playlist->object_lock );
998 vlc_object_release( p_playlist );
999 [self playlistUpdated];
1002 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
1004 intf_thread_t * p_intf = VLCIntf;
1005 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1008 if( p_playlist == NULL )
1012 playlist_item_t *p_item;
1014 BOOL b_rem = FALSE, b_dir = FALSE;
1015 NSString *o_uri, *o_name;
1020 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1021 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1022 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1024 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1025 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1027 int i_count, i_index;
1028 struct statfs *mounts = NULL;
1030 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1031 /* getmntinfo returns a pointer to static data. Do not free. */
1032 for( i_index = 0 ; i_index < i_count; i_index++ )
1034 NSMutableString *o_temp, *o_temp2;
1035 o_temp = [NSMutableString stringWithString: o_uri];
1036 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1037 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1038 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1039 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1041 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1043 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1047 /* If no name, then make a guess */
1048 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1050 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1051 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1052 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1054 /* All of this is to make sure CD's play when you D&D them on VLC */
1055 /* Converts mountpoint to a /dev file */
1058 NSMutableString *o_temp;
1060 buf = (struct statfs *) malloc (sizeof(struct statfs));
1061 statfs( [o_uri fileSystemRepresentation], buf );
1062 psz_dev = strdup(buf->f_mntfromname);
1063 o_temp = [NSMutableString stringWithCString: psz_dev ];
1064 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1065 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1066 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1070 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1076 for( i = 0; i < (int)[o_options count]; i++ )
1078 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1082 /* Recent documents menu */
1083 o_true_file = [NSURL fileURLWithPath: o_uri];
1084 if( o_true_file != nil )
1086 [[NSDocumentController sharedDocumentController]
1087 noteNewRecentDocumentURL: o_true_file];
1090 vlc_object_release( p_playlist );
1094 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1097 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1099 if( p_playlist == NULL )
1104 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1106 playlist_item_t *p_item;
1107 NSDictionary *o_one_item;
1110 o_one_item = [o_array objectAtIndex: i_item];
1111 p_item = [self createItem: o_one_item];
1118 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1120 if( i_item == 0 && !b_enqueue )
1122 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1125 vlc_object_release( p_playlist );
1128 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1131 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1133 if( p_playlist == NULL )
1138 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1140 playlist_item_t *p_item;
1141 NSDictionary *o_one_item;
1144 o_one_item = [o_array objectAtIndex: i_item];
1145 p_item = [self createItem: o_one_item];
1152 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1154 if( i_item == 0 && !b_enqueue )
1156 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1159 vlc_object_release( p_playlist );
1163 - (IBAction)handlePopUp:(id)sender
1166 intf_thread_t * p_intf = VLCIntf;
1167 vlc_value_t val1,val2;
1168 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1170 if( p_playlist == NULL )
1175 switch( [o_loop_popup indexOfSelectedItem] )
1180 var_Set( p_playlist, "loop", val1 );
1182 var_Set( p_playlist, "repeat", val1 );
1183 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1188 var_Set( p_playlist, "repeat", val1 );
1190 var_Set( p_playlist, "loop", val1 );
1191 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1195 var_Get( p_playlist, "repeat", &val1 );
1196 var_Get( p_playlist, "loop", &val2 );
1197 if( val1.b_bool || val2.b_bool )
1200 var_Set( p_playlist, "repeat", val1 );
1201 var_Set( p_playlist, "loop", val1 );
1202 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1206 vlc_object_release( p_playlist );
1207 [self playlistUpdated];
1210 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1212 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1214 playlist_item_t *p_selected_item;
1215 int i_current, i_selected_row;
1220 i_selected_row = [o_outline_view selectedRow];
1221 if (i_selected_row < 0)
1224 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1225 i_selected_row] pointerValue];
1227 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1230 NSString *o_current_name, *o_current_author;
1232 vlc_mutex_lock( &p_playlist->object_lock );
1233 o_current_name = [NSString stringWithUTF8String:
1234 p_item->pp_children[i_current]->input.psz_name];
1235 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1236 _("Meta-information"),_("Artist") );
1237 o_current_author = [NSString stringWithUTF8String: psz_temp];
1239 vlc_mutex_unlock( &p_playlist->object_lock );
1241 if( p_selected_item == p_item->pp_children[i_current] &&
1242 b_selected_item_met == NO )
1244 b_selected_item_met = YES;
1246 else if( p_selected_item == p_item->pp_children[i_current] &&
1247 b_selected_item_met == YES )
1249 vlc_object_release( p_playlist );
1252 else if( b_selected_item_met == YES &&
1253 ( [o_current_name rangeOfString:[o_search_field
1254 stringValue] options:NSCaseInsensitiveSearch ].length ||
1255 [o_current_author rangeOfString:[o_search_field
1256 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1258 vlc_object_release( p_playlist );
1259 /*Adds the parent items in the result array as well, so that we can
1261 return [NSMutableArray arrayWithObject: [NSValue
1262 valueWithPointer: p_item->pp_children[i_current]]];
1264 if( p_item->pp_children[i_current]->i_children > 0 )
1266 id o_result = [self subSearchItem:
1267 p_item->pp_children[i_current]];
1268 if( o_result != NULL )
1270 vlc_object_release( p_playlist );
1271 [o_result insertObject: [NSValue valueWithPointer:
1272 p_item->pp_children[i_current]] atIndex:0];
1277 vlc_object_release( p_playlist );
1281 - (IBAction)searchItem:(id)sender
1283 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1285 playlist_view_t * p_view;
1291 b_selected_item_met = NO;
1293 if( p_playlist == NULL )
1295 p_view = playlist_ViewFind( p_playlist, i_current_view );
1299 /*First, only search after the selected item:*
1300 *(b_selected_item_met = NO) */
1301 o_result = [self subSearchItem:p_view->p_root];
1302 if( o_result == NULL )
1304 /* If the first search failed, search again from the beginning */
1305 o_result = [self subSearchItem:p_view->p_root];
1307 if( o_result != NULL )
1310 if( [[o_result objectAtIndex: 0] pointerValue] ==
1311 p_playlist->p_general )
1316 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1318 [o_outline_view expandItem: [o_outline_dict objectForKey:
1319 [NSString stringWithFormat: @"%p",
1320 [[o_result objectAtIndex: i] pointerValue]]]];
1322 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1323 [NSString stringWithFormat: @"%p",
1324 [[o_result objectAtIndex: [o_result count] - 1 ]
1329 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1330 [o_outline_view scrollRowToVisible: i_row];
1333 vlc_object_release( p_playlist );
1336 - (IBAction)recursiveExpandNode:(id)sender
1339 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1340 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1342 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1343 isItemExpandable: o_item] )
1345 for( i = 0 ; i < p_item->i_parents ; i++ )
1347 if( p_item->pp_parents[i]->i_view == i_current_view )
1349 o_item = [o_outline_dict objectForKey: [NSString
1350 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1356 /* We need to collapse the node first, since OSX refuses to recursively
1357 expand an already expanded node, even if children nodes are collapsed. */
1358 [o_outline_view collapseItem: o_item collapseChildren: YES];
1359 [o_outline_view expandItem: o_item expandChildren: YES];
1362 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1366 vlc_bool_t b_item_sel;
1368 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1370 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1371 [o_outline_view selectedRow] != -1 );
1372 b_rows = [o_outline_view numberOfRows] != 0;
1374 [o_mi_play setEnabled: b_item_sel];
1375 [o_mi_delete setEnabled: b_item_sel];
1376 [o_mi_selectall setEnabled: b_rows];
1377 [o_mi_info setEnabled: b_item_sel];
1378 [o_mi_preparse setEnabled: b_item_sel];
1379 [o_mi_recursive_expand setEnabled: b_item_sel];
1380 [o_mi_sort_name setEnabled: b_item_sel];
1381 [o_mi_sort_author setEnabled: b_item_sel];
1383 return( o_ctx_menu );
1386 - (void)outlineView: (NSTableView*)o_tv
1387 didClickTableColumn:(NSTableColumn *)o_tc
1389 int i_mode = 0, i_type;
1390 intf_thread_t *p_intf = VLCIntf;
1391 playlist_view_t *p_view;
1393 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1395 if( p_playlist == NULL )
1400 /* Check whether the selected table column header corresponds to a
1401 sortable table column*/
1402 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1404 vlc_object_release( p_playlist );
1408 p_view = playlist_ViewFind( p_playlist, i_current_view );
1410 if( o_tc_sortColumn == o_tc )
1412 b_isSortDescending = !b_isSortDescending;
1416 b_isSortDescending = VLC_FALSE;
1419 if( o_tc == o_tc_name )
1421 i_mode = SORT_TITLE;
1423 else if( o_tc == o_tc_author )
1425 i_mode = SORT_AUTHOR;
1428 if( b_isSortDescending )
1430 i_type = ORDER_REVERSE;
1434 i_type = ORDER_NORMAL;
1437 vlc_mutex_lock( &p_playlist->object_lock );
1438 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1439 vlc_mutex_unlock( &p_playlist->object_lock );
1441 vlc_object_release( p_playlist );
1442 [self playlistUpdated];
1444 o_tc_sortColumn = o_tc;
1445 [o_outline_view setHighlightedTableColumn:o_tc];
1447 if( b_isSortDescending )
1449 [o_outline_view setIndicatorImage:o_descendingSortingImage
1450 inTableColumn:o_tc];
1454 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1455 inTableColumn:o_tc];
1460 - (void)outlineView:(NSOutlineView *)outlineView
1461 willDisplayCell:(id)cell
1462 forTableColumn:(NSTableColumn *)tableColumn
1465 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1470 if( !p_playlist ) return;
1472 o_playing_item = [o_outline_dict objectForKey:
1473 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1475 if( [self isItem: [o_playing_item pointerValue] inNode:
1476 [item pointerValue] checkItemExistence: YES]
1477 || [o_playing_item isEqual: item] )
1479 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1483 [cell setFont: [NSFont systemFontOfSize: 0]];
1485 vlc_object_release( p_playlist );
1490 @implementation VLCPlaylist (NSOutlineViewDataSource)
1492 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1494 id o_value = [super outlineView: outlineView child: index ofItem: item];
1495 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1498 if( !p_playlist ) return nil;
1500 if( p_playlist->i_size >= 2 )
1502 [o_status_field setStringValue: [NSString stringWithFormat:
1503 _NS("%i items in playlist"), p_playlist->i_size]];
1507 if( p_playlist->i_size == 0 )
1509 [o_status_field setStringValue: [NSString stringWithFormat:
1510 _NS("no items in playlist"), p_playlist->i_size]];
1514 [o_status_field setStringValue: [NSString stringWithFormat:
1515 _NS("1 item in playlist"), p_playlist->i_size]];
1518 vlc_object_release( p_playlist );
1520 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1521 [o_value pointerValue]]];
1527 /* Required for drag & drop and reordering */
1528 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1531 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1534 /* First remove the items that were moved during the last drag & drop
1536 [o_items_array removeAllObjects];
1537 [o_nodes_array removeAllObjects];
1539 if( !p_playlist ) return NO;
1541 for( i = 0 ; i < [items count] ; i++ )
1543 id o_item = [items objectAtIndex: i];
1545 /* Refuse to move items that are not in the General Node
1546 (Service Discovery) */
1547 if( ![self isItem: [o_item pointerValue] inNode:
1548 p_playlist->p_general checkItemExistence: NO])
1550 vlc_object_release(p_playlist);
1553 /* Fill the items and nodes to move in 2 different arrays */
1554 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1555 [o_nodes_array addObject: o_item];
1557 [o_items_array addObject: o_item];
1560 /* Now we need to check if there are selected items that are in already
1561 selected nodes. In that case, we only want to move the nodes */
1562 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1563 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1568 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1570 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1572 if( j == i ) continue;
1573 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1574 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1576 [o_nodes_array removeObjectAtIndex:i];
1577 /* We need to execute the next iteration with the same index
1578 since the current item has been deleted */
1585 for( i = 0 ; i < [o_items_array count] ; i++ )
1587 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1589 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1590 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1592 [o_items_array removeObjectAtIndex:i];
1599 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1600 a Drop operation comçing from the playlist.
1601 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1604 [pboard declareTypes: [NSArray arrayWithObjects:
1605 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1606 [pboard setPropertyList:[NSArray array]
1607 forType:NSFilenamesPboardType];
1609 vlc_object_release(p_playlist);
1613 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1615 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1617 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1619 if( !p_playlist ) return NSDragOperationNone;
1621 /* We refuse to drop an item in anything else than a child of the General
1622 Node. We still accept items that would be root nodes of the outlineview
1623 however, to allow drop in an empty playlist.*/
1624 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1625 checkItemExistence: NO] || item == nil) )
1627 vlc_object_release(p_playlist);
1628 return NSDragOperationNone;
1631 /* Drop from the Playlist */
1632 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1635 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1637 /* We refuse to Drop in a child of an item we are moving */
1638 if( [self isItem: [item pointerValue] inNode:
1639 [[o_nodes_array objectAtIndex: i] pointerValue]
1640 checkItemExistence: NO] )
1642 vlc_object_release(p_playlist);
1643 return NSDragOperationNone;
1646 vlc_object_release(p_playlist);
1647 return NSDragOperationMove;
1650 /* Drop from the Finder */
1651 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1653 vlc_object_release(p_playlist);
1654 return NSDragOperationGeneric;
1656 vlc_object_release(p_playlist);
1657 return NSDragOperationNone;
1660 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1662 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1664 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1666 if( !p_playlist ) return NO;
1668 /* Drag & Drop inside the playlist */
1669 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1673 playlist_item_t *p_new_parent, *p_item = NULL;
1674 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1676 /* If the item is to be dropped as root item of the outline, make it a
1677 child of the General node.
1678 Else, choose the proposed parent as parent. */
1680 p_new_parent = p_playlist->p_general;
1682 p_new_parent = [item pointerValue];
1684 /* If the proposed parent is not a node, then use the parent node of
1686 if( p_new_parent->i_children <= 0 )
1689 playlist_item_t *p_temp_item = p_new_parent;
1690 p_new_parent = [self parentOfItem: p_new_parent];
1693 vlc_object_release(p_playlist);
1696 /* Calculate the position of the dropped item in this new parent:
1697 following the first proposed parent. */
1698 for( j = 0; j < p_new_parent->i_children; j++ )
1700 if( p_new_parent->pp_children[j] == p_temp_item )
1705 else if( j == p_new_parent->i_children - 1 )
1710 for( i = 0; i < [o_all_items count]; i++ )
1712 playlist_item_t *p_old_parent = NULL;
1713 int i_old_index = 0;
1715 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1716 p_old_parent = [self parentOfItem: p_item];
1719 /* We may need the old index later */
1720 if( p_new_parent == p_old_parent )
1723 for( j = 0; j < p_old_parent->i_children; j++ )
1725 if( p_old_parent->pp_children[j] == p_item )
1734 /* If we move the playing item in a different node or we move the
1735 node containing the playing item in a different node, then stop
1736 playback, or the playlist refuses to detach the item. */
1737 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1738 (( p_item == p_playlist->status.p_item &&
1739 p_new_parent != p_old_parent) ||
1740 ( p_item->i_children > 0 &&
1741 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1743 playlist_Stop( p_playlist );
1745 vlc_mutex_lock( &p_playlist->object_lock );
1746 // Acually detach the item from the old position
1747 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1749 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1753 /* Calculate the new index */
1756 /* If we move the item in the same node, we need to take into
1757 account that one item will be deleted */
1758 else if((p_new_parent == p_old_parent &&
1759 i_old_index < index + (int)i)
1760 || p_new_parent == p_playlist->p_general || index == 0 )
1761 i_new_index = index + i;
1763 i_new_index = index + i + 1;
1764 // Reattach the item to the new position
1765 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1766 p_new_parent, i_new_index );
1768 vlc_mutex_unlock( &p_playlist->object_lock );
1770 [self playlistUpdated];
1771 i_row = [o_outline_view rowForItem:[o_outline_dict
1772 objectForKey:[NSString stringWithFormat: @"%p",
1773 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1777 i_row = [o_outline_view rowForItem:[o_outline_dict
1778 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1781 [o_outline_view deselectAll: self];
1782 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1783 [o_outline_view scrollRowToVisible: i_row];
1785 vlc_object_release(p_playlist);
1789 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1792 playlist_item_t *p_node = [item pointerValue];
1794 NSArray *o_array = [NSArray array];
1795 NSArray *o_values = [[o_pasteboard propertyListForType:
1796 NSFilenamesPboardType]
1797 sortedArrayUsingSelector:
1798 @selector(caseInsensitiveCompare:)];
1800 for( i = 0; i < (int)[o_values count]; i++)
1802 NSDictionary *o_dic;
1803 o_dic = [NSDictionary dictionaryWithObject:[o_values
1804 objectAtIndex:i] forKey:@"ITEM_URL"];
1805 o_array = [o_array arrayByAddingObject: o_dic];
1810 [self appendArray: o_array atPos: index enqueue: YES];
1812 else if( p_node->i_children == -1 )
1815 playlist_item_t *p_real_node = NULL;
1817 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1819 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1821 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1824 if( i_counter == p_node->i_parents )
1826 vlc_object_release(p_playlist);
1830 [self appendNodeArray: o_array inNode: p_real_node
1831 atPos: index inView: i_current_view enqueue: YES];
1835 [self appendNodeArray: o_array inNode: p_node
1836 atPos: index inView: i_current_view enqueue: YES];
1838 vlc_object_release( p_playlist );
1841 vlc_object_release( p_playlist );
1845 /* Delegate method of NSWindow */
1846 /*- (void)windowWillClose:(NSNotification *)aNotification
1848 [o_btn_playlist setState: NSOffState];