1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2006 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create toggle buttons for the shuffle, repeat one, repeat all functions.
29 * reimplement enable/disable item
30 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
31 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
35 /*****************************************************************************
37 *****************************************************************************/
38 #include <stdlib.h> /* malloc(), free() */
39 #include <sys/param.h> /* for MAXPATHLEN */
42 #include <sys/mount.h>
48 #import "playlistinfo.h"
54 /*****************************************************************************
55 * VLCPlaylistView implementation
56 *****************************************************************************/
57 @implementation VLCPlaylistView
59 - (NSMenu *)menuForEvent:(NSEvent *)o_event
61 return( [[self delegate] menuForEvent: o_event] );
64 - (void)keyDown:(NSEvent *)o_event
68 if( [[o_event characters] length] )
70 key = [[o_event characters] characterAtIndex: 0];
75 case NSDeleteCharacter:
76 case NSDeleteFunctionKey:
77 case NSDeleteCharFunctionKey:
78 case NSBackspaceCharacter:
79 [[self delegate] deleteItem:self];
82 case NSEnterCharacter:
83 case NSCarriageReturnCharacter:
84 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
89 [super keyDown: o_event];
97 /*****************************************************************************
98 * VLCPlaylistCommon implementation
100 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
101 * It contains the common methods and elements of these 2 entities.
102 *****************************************************************************/
103 @implementation VLCPlaylistCommon
110 o_outline_dict = [[NSMutableDictionary alloc] init];
116 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
118 i_current_view = VIEW_CATEGORY;
119 playlist_ViewUpdate( p_playlist, i_current_view );
121 [o_outline_view setTarget: self];
122 [o_outline_view setDelegate: self];
123 [o_outline_view setDataSource: self];
125 vlc_object_release( p_playlist );
131 [[o_tc_name headerCell] setStringValue:_NS("Name")];
132 [[o_tc_author headerCell] setStringValue:_NS("Author")];
133 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
136 - (NSOutlineView *)outlineView
138 return o_outline_view;
141 - (playlist_item_t *)selectedPlaylistItem
143 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
149 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
151 /* return the number of children for Obj-C pointer item */ /* DONE */
152 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
155 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
157 if( p_playlist == NULL )
159 if( outlineView != o_outline_view )
161 vlc_object_release( p_playlist );
168 playlist_view_t *p_view;
169 p_view = playlist_ViewFind( p_playlist, i_current_view );
170 if( p_view && p_view->p_root )
172 i_return = p_view->p_root->i_children;
174 if( i_current_view == VIEW_CATEGORY )
176 i_return--; /* remove the GENERAL item from the list */
177 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
183 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
185 i_return = p_item->i_children;
187 vlc_object_release( p_playlist );
195 /* return the child at index for the Obj-C pointer item */ /* DONE */
196 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
198 playlist_item_t *p_return = NULL;
199 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
203 if( p_playlist == NULL )
209 playlist_view_t *p_view;
210 p_view = playlist_ViewFind( p_playlist, i_current_view );
211 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
213 if( i_current_view == VIEW_CATEGORY )
215 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
217 p_return = p_playlist->p_general->pp_children[index];
219 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
221 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
227 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
228 if( p_item && index < p_item->i_children && index >= 0 )
229 p_return = p_item->pp_children[index];
233 vlc_object_release( p_playlist );
235 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
238 o_value = [[NSValue valueWithPointer: p_return] retain];
243 /* is the item expandable */
244 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
247 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
249 if( p_playlist == NULL )
255 playlist_view_t *p_view;
256 p_view = playlist_ViewFind( p_playlist, i_current_view );
257 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
259 if( i_current_view == VIEW_CATEGORY )
262 i_return += p_playlist->p_general->i_children;
267 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
269 i_return = p_item->i_children;
271 vlc_object_release( p_playlist );
279 /* retrieve the string values for the cells */
280 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
283 intf_thread_t *p_intf = VLCIntf;
284 playlist_t *p_playlist;
285 playlist_item_t *p_item;
287 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
289 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
291 if( p_playlist == NULL )
296 p_item = (playlist_item_t *)[item pointerValue];
300 vlc_object_release( p_playlist );
304 if( [[o_tc identifier] isEqualToString:@"1"] )
306 o_value = [NSString stringWithUTF8String:
307 p_item->input.psz_name];
308 if( o_value == NULL )
309 o_value = [NSString stringWithCString:
310 p_item->input.psz_name];
312 else if( [[o_tc identifier] isEqualToString:@"2"] )
315 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
317 if( psz_temp == NULL )
321 o_value = [NSString stringWithUTF8String: psz_temp];
322 if( o_value == NULL )
324 o_value = [NSString stringWithCString: psz_temp];
329 else if( [[o_tc identifier] isEqualToString:@"3"] )
331 char psz_duration[MSTRTIME_MAX_SIZE];
332 mtime_t dur = p_item->input.i_duration;
335 secstotimestr( psz_duration, dur/1000000 );
336 o_value = [NSString stringWithUTF8String: psz_duration];
340 o_value = @"-:--:--";
343 vlc_object_release( p_playlist );
350 /*****************************************************************************
351 * VLCPlaylistWizard implementation
352 *****************************************************************************/
353 @implementation VLCPlaylistWizard
355 - (IBAction)reloadOutlineView
357 /* Only reload the outlineview if the wizard window is open since this can
358 be quite long on big playlists */
359 if( [[o_outline_view window] isVisible] )
361 [o_outline_view reloadData];
367 /*****************************************************************************
368 * VLCPlaylist implementation
369 *****************************************************************************/
370 @implementation VLCPlaylist
377 o_nodes_array = [[NSMutableArray alloc] init];
378 o_items_array = [[NSMutableArray alloc] init];
385 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
387 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
392 [super awakeFromNib];
394 [o_outline_view setDoubleAction: @selector(playItem:)];
396 [o_outline_view registerForDraggedTypes:
397 [NSArray arrayWithObjects: NSFilenamesPboardType,
398 @"VLCPlaylistItemPboardType", 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 //[self playlistUpdated];
468 - (void)searchfieldChanged:(NSNotification *)o_notification
470 [o_search_field setStringValue:[[o_notification object] stringValue]];
477 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
478 [o_mi_play setTitle: _NS("Play")];
479 [o_mi_delete setTitle: _NS("Delete")];
480 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
481 [o_mi_selectall setTitle: _NS("Select All")];
482 [o_mi_info setTitle: _NS("Information")];
483 [o_mi_preparse setTitle: _NS("Get Stream Information")];
484 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
485 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
486 [o_mi_services setTitle: _NS("Services discovery")];
487 [o_status_field setStringValue: [NSString stringWithFormat:
488 _NS("No items in the playlist")]];
490 [o_random_ckb setTitle: _NS("Random")];
492 [o_search_button setTitle: _NS("Search")];
494 [o_search_field setToolTip: _NS("Search in Playlist")];
495 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
496 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
497 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
498 [o_mi_addNode setTitle: _NS("Add Folder to Playlist")];
500 [o_save_accessory_text setStringValue: _NS("File Format:")];
501 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
502 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
505 - (void)playlistUpdated
509 /* Clear indications of any existing column sorting */
510 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
512 [o_outline_view setIndicatorImage:nil inTableColumn:
513 [[o_outline_view tableColumns] objectAtIndex:i]];
516 [o_outline_view setHighlightedTableColumn:nil];
517 o_tc_sortColumn = nil;
518 // TODO Find a way to keep the dict size to a minimum
519 //[o_outline_dict removeAllObjects];
520 [o_outline_view reloadData];
521 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
522 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
524 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
529 if( p_playlist->i_size >= 2 )
531 [o_status_field setStringValue: [NSString stringWithFormat:
532 _NS("%i items in the playlist"), p_playlist->i_size]];
536 if( p_playlist->i_size == 0 )
538 [o_status_field setStringValue: _NS("No items in the playlist")];
542 [o_status_field setStringValue: _NS("1 item in the playlist")];
545 vlc_object_release( p_playlist );
548 - (void)playModeUpdated
550 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
552 vlc_value_t val, val2;
554 if( p_playlist == NULL )
559 var_Get( p_playlist, "loop", &val2 );
560 var_Get( p_playlist, "repeat", &val );
561 if( val.b_bool == VLC_TRUE )
563 [o_loop_popup selectItemAtIndex: 1];
565 else if( val2.b_bool == VLC_TRUE )
567 [o_loop_popup selectItemAtIndex: 2];
571 [o_loop_popup selectItemAtIndex: 0];
574 var_Get( p_playlist, "random", &val );
575 [o_random_ckb setState: val.b_bool];
577 vlc_object_release( p_playlist );
580 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
583 for( i = 0 ; i < p_item->i_parents; i++ )
585 if( p_item->pp_parents[i]->i_view == i_current_view )
587 return p_item->pp_parents[i]->p_parent;
593 - (void)updateRowSelection
598 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
600 playlist_item_t *p_item, *p_temp_item;
601 NSMutableArray *o_array = [NSMutableArray array];
603 if( p_playlist == NULL )
606 p_item = p_playlist->status.p_item;
609 vlc_object_release(p_playlist);
613 p_temp_item = p_item;
614 while( p_temp_item->i_parents > 0 )
616 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
618 p_temp_item = [self parentOfItem: p_temp_item];
619 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
621 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
623 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
629 for (j = 0 ; j < [o_array count] - 1 ; j++)
632 if( ( o_item = [o_outline_dict objectForKey:
633 [NSString stringWithFormat: @"%p",
634 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
635 [o_outline_view expandItem: o_item];
639 i_row = [o_outline_view rowForItem:[o_outline_dict
640 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
642 [o_outline_view selectRow: i_row byExtendingSelection: NO];
643 [o_outline_view scrollRowToVisible: i_row];
645 vlc_object_release(p_playlist);
647 /* update our info-panel to reflect the new item */
648 [[[VLCMain sharedInstance] getInfo] updatePanel];
651 /* Check if p_item is a child of p_node recursively. We need to check the item
652 existence first since OSX sometimes tries to redraw items that have been
653 deleted. We don't do it when not required since this verification takes
654 quite a long time on big playlists (yes, pretty hacky). */
655 - (BOOL)isItem: (playlist_item_t *)p_item
656 inNode: (playlist_item_t *)p_node
657 checkItemExistence:(BOOL)b_check
660 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
662 playlist_item_t *p_temp_item = p_item;
664 if( p_playlist == NULL )
669 if( p_node == p_item )
671 vlc_object_release(p_playlist);
675 if( p_node->i_children < 1)
677 vlc_object_release(p_playlist);
684 vlc_mutex_lock( &p_playlist->object_lock );
688 /* Since outlineView: willDisplayCell:... may call this function with
689 p_items that don't exist anymore, first check if the item is still
690 in the playlist. Any cleaner solution welcomed. */
691 for( i = 0; i < p_playlist->i_all_size; i++ )
693 if( p_playlist->pp_all_items[i] == p_item ) break;
694 else if ( i == p_playlist->i_all_size - 1 )
696 vlc_object_release( p_playlist );
697 vlc_mutex_unlock( &p_playlist->object_lock );
703 while( p_temp_item->i_parents > 0 )
705 p_temp_item = [self parentOfItem: p_temp_item];
706 if( p_temp_item == p_node )
708 vlc_mutex_unlock( &p_playlist->object_lock );
709 vlc_object_release( p_playlist );
713 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
715 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
717 if( p_temp_item->pp_parents[i]->p_parent == p_node )
719 vlc_mutex_unlock( &p_playlist->object_lock );
720 vlc_object_release( p_playlist );
725 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
731 vlc_mutex_unlock( &p_playlist->object_lock );
734 vlc_object_release( p_playlist );
738 /* This method is usefull for instance to remove the selected children of an
739 already selected node */
740 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
743 for( i = 0 ; i < [o_items count] ; i++ )
745 for ( j = 0 ; j < [o_nodes count] ; j++ )
747 if( o_items == o_nodes)
749 if( j == i ) continue;
751 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
752 inNode: [[o_nodes objectAtIndex:j] pointerValue]
753 checkItemExistence: NO] )
755 [o_items removeObjectAtIndex:i];
756 /* We need to execute the next iteration with the same index
757 since the current item has been deleted */
766 - (IBAction)savePlaylist:(id)sender
768 intf_thread_t * p_intf = VLCIntf;
769 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
772 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
773 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
775 //[o_save_panel setAllowedFileTypes: [NSArray arrayWithObjects: @"m3u", @"xpf", nil] ];
776 [o_save_panel setTitle: _NS("Save Playlist")];
777 [o_save_panel setPrompt: _NS("Save")];
778 [o_save_panel setAccessoryView: o_save_accessory_view];
780 if( [o_save_panel runModalForDirectory: nil
781 file: o_name] == NSOKButton )
783 NSString *o_filename = [o_save_panel filename];
785 if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
787 NSString * o_real_filename;
789 range.location = [o_filename length] - [@".xspf" length];
790 range.length = [@".xspf" length];
792 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
793 range: range] != NSOrderedSame )
795 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
799 o_real_filename = o_filename;
801 playlist_Export( p_playlist, [o_real_filename fileSystemRepresentation], "export-xspf" );
805 NSString * o_real_filename;
807 range.location = [o_filename length] - [@".m3u" length];
808 range.length = [@".m3u" length];
810 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
811 range: range] != NSOrderedSame )
813 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
817 o_real_filename = o_filename;
819 playlist_Export( p_playlist, [o_real_filename fileSystemRepresentation], "export-m3u" );
822 vlc_object_release( p_playlist );
825 /* When called retrieves the selected outlineview row and plays that node or item */
826 - (IBAction)playItem:(id)sender
828 intf_thread_t * p_intf = VLCIntf;
829 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
832 if( p_playlist != NULL )
834 playlist_item_t *p_item;
835 playlist_item_t *p_node = NULL;
837 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
841 if( p_item->i_children == -1 )
843 p_node = [self parentOfItem: p_item];
845 /* for( i = 0 ; i < p_item->i_parents ; i++ )
847 if( p_item->pp_parents[i]->i_view == i_current_view )
849 p_node = p_item->pp_parents[i]->p_parent;
856 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
858 p_item = p_node->pp_children[0];
865 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
867 vlc_object_release( p_playlist );
871 /* When called retrieves the selected outlineview row and plays that node or item */
872 - (IBAction)preparseItem:(id)sender
875 NSMutableArray *o_to_preparse;
876 intf_thread_t * p_intf = VLCIntf;
877 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
880 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
881 i_count = [o_to_preparse count];
883 if( p_playlist != NULL )
887 playlist_item_t *p_item = NULL;
889 for( i = 0; i < i_count; i++ )
891 o_number = [o_to_preparse lastObject];
892 i_row = [o_number intValue];
893 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
894 [o_to_preparse removeObject: o_number];
895 [o_outline_view deselectRow: i_row];
899 if( p_item->i_children == -1 )
901 playlist_PreparseEnqueue( p_playlist, &p_item->input );
905 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
909 vlc_object_release( p_playlist );
911 [self playlistUpdated];
914 - (IBAction)servicesChange:(id)sender
916 NSMenuItem *o_mi = (NSMenuItem *)sender;
917 NSString *o_string = [o_mi representedObject];
918 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
920 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
921 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
923 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
925 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
926 [o_string cString] ) ? YES : NO];
928 i_current_view = VIEW_CATEGORY;
929 playlist_ViewUpdate( p_playlist, i_current_view );
930 vlc_object_release( p_playlist );
931 [self playlistUpdated];
935 - (IBAction)selectAll:(id)sender
937 [o_outline_view selectAll: nil];
940 - (IBAction)deleteItem:(id)sender
942 int i, i_count, i_row;
943 NSMutableArray *o_to_delete;
946 playlist_t * p_playlist;
947 intf_thread_t * p_intf = VLCIntf;
949 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
952 if ( p_playlist == NULL )
956 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
957 i_count = [o_to_delete count];
959 for( i = 0; i < i_count; i++ )
961 o_number = [o_to_delete lastObject];
962 i_row = [o_number intValue];
963 id o_item = [o_outline_view itemAtRow: i_row];
964 playlist_item_t *p_item = [o_item pointerValue];
965 [o_to_delete removeObject: o_number];
966 [o_outline_view deselectRow: i_row];
968 if( [[o_outline_view dataSource] outlineView:o_outline_view
969 numberOfChildrenOfItem: o_item] > 0 )
970 //is a node and not an item
972 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
973 [self isItem: p_playlist->status.p_item inNode:
974 ((playlist_item_t *)[o_item pointerValue])
975 checkItemExistence: NO] == YES )
977 // if current item is in selected node and is playing then stop playlist
978 playlist_Stop( p_playlist );
980 vlc_mutex_lock( &p_playlist->object_lock );
981 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
982 vlc_mutex_unlock( &p_playlist->object_lock );
986 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
987 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
989 playlist_Stop( p_playlist );
991 vlc_mutex_lock( &p_playlist->object_lock );
992 playlist_Delete( p_playlist, p_item->input.i_id );
993 vlc_mutex_unlock( &p_playlist->object_lock );
996 [self playlistUpdated];
997 vlc_object_release( p_playlist );
1000 - (IBAction)sortNodeByName:(id)sender
1002 [self sortNode: SORT_TITLE];
1005 - (IBAction)sortNodeByAuthor:(id)sender
1007 [self sortNode: SORT_AUTHOR];
1010 - (void)sortNode:(int)i_mode
1012 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1014 playlist_item_t * p_item;
1016 if (p_playlist == NULL)
1021 if( [o_outline_view selectedRow] > -1 )
1023 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
1027 /*If no item is selected, sort the whole playlist*/
1029 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
1030 p_item = p_view->p_root;
1033 if( p_item->i_children > -1 ) // the item is a node
1035 vlc_mutex_lock( &p_playlist->object_lock );
1036 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
1037 vlc_mutex_unlock( &p_playlist->object_lock );
1043 for( i = 0 ; i < p_item->i_parents ; i++ )
1045 if( p_item->pp_parents[i]->i_view == i_current_view )
1047 vlc_mutex_lock( &p_playlist->object_lock );
1048 playlist_RecursiveNodeSort( p_playlist,
1049 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
1050 vlc_mutex_unlock( &p_playlist->object_lock );
1055 vlc_object_release( p_playlist );
1056 [self playlistUpdated];
1059 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
1061 intf_thread_t * p_intf = VLCIntf;
1062 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1065 if( p_playlist == NULL )
1069 playlist_item_t *p_item;
1071 BOOL b_rem = FALSE, b_dir = FALSE;
1072 NSString *o_uri, *o_name;
1077 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1078 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1079 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1081 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1082 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1084 int i_count, i_index;
1085 struct statfs *mounts = NULL;
1087 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1088 /* getmntinfo returns a pointer to static data. Do not free. */
1089 for( i_index = 0 ; i_index < i_count; i_index++ )
1091 NSMutableString *o_temp, *o_temp2;
1092 o_temp = [NSMutableString stringWithString: o_uri];
1093 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1094 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1095 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1096 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1098 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1100 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1104 /* If no name, then make a guess */
1105 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1107 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1108 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1109 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1111 /* All of this is to make sure CD's play when you D&D them on VLC */
1112 /* Converts mountpoint to a /dev file */
1115 NSMutableString *o_temp;
1117 buf = (struct statfs *) malloc (sizeof(struct statfs));
1118 statfs( [o_uri fileSystemRepresentation], buf );
1119 psz_dev = strdup(buf->f_mntfromname);
1120 o_temp = [NSMutableString stringWithCString: psz_dev ];
1121 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1122 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1123 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1127 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1133 for( i = 0; i < (int)[o_options count]; i++ )
1135 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1139 /* Recent documents menu */
1140 o_true_file = [NSURL fileURLWithPath: o_uri];
1141 if( o_true_file != nil )
1143 [[NSDocumentController sharedDocumentController]
1144 noteNewRecentDocumentURL: o_true_file];
1147 vlc_object_release( p_playlist );
1151 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1154 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1156 if( p_playlist == NULL )
1161 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1163 playlist_item_t *p_item;
1164 NSDictionary *o_one_item;
1167 o_one_item = [o_array objectAtIndex: i_item];
1168 p_item = [self createItem: o_one_item];
1175 playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1177 if( i_item == 0 && !b_enqueue )
1179 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1182 vlc_object_release( p_playlist );
1185 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1188 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1190 if( p_playlist == NULL )
1195 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1197 playlist_item_t *p_item;
1198 NSDictionary *o_one_item;
1201 o_one_item = [o_array objectAtIndex: i_item];
1202 p_item = [self createItem: o_one_item];
1209 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
1211 if( i_item == 0 && !b_enqueue )
1213 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1216 vlc_object_release( p_playlist );
1220 - (IBAction)handlePopUp:(id)sender
1223 intf_thread_t * p_intf = VLCIntf;
1224 vlc_value_t val1,val2;
1225 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1227 if( p_playlist == NULL )
1232 switch( [o_loop_popup indexOfSelectedItem] )
1237 var_Set( p_playlist, "loop", val1 );
1239 var_Set( p_playlist, "repeat", val1 );
1240 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1245 var_Set( p_playlist, "repeat", val1 );
1247 var_Set( p_playlist, "loop", val1 );
1248 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1252 var_Get( p_playlist, "repeat", &val1 );
1253 var_Get( p_playlist, "loop", &val2 );
1254 if( val1.b_bool || val2.b_bool )
1257 var_Set( p_playlist, "repeat", val1 );
1258 var_Set( p_playlist, "loop", val1 );
1259 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1263 vlc_object_release( p_playlist );
1264 [self playlistUpdated];
1267 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1269 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1271 playlist_item_t *p_selected_item;
1272 int i_current, i_selected_row;
1277 i_selected_row = [o_outline_view selectedRow];
1278 if (i_selected_row < 0)
1281 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1282 i_selected_row] pointerValue];
1284 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1287 NSString *o_current_name, *o_current_author;
1289 vlc_mutex_lock( &p_playlist->object_lock );
1290 o_current_name = [NSString stringWithUTF8String:
1291 p_item->pp_children[i_current]->input.psz_name];
1292 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1293 _("Meta-information"),_("Artist") );
1294 o_current_author = [NSString stringWithUTF8String: psz_temp];
1296 vlc_mutex_unlock( &p_playlist->object_lock );
1298 if( p_selected_item == p_item->pp_children[i_current] &&
1299 b_selected_item_met == NO )
1301 b_selected_item_met = YES;
1303 else if( p_selected_item == p_item->pp_children[i_current] &&
1304 b_selected_item_met == YES )
1306 vlc_object_release( p_playlist );
1309 else if( b_selected_item_met == YES &&
1310 ( [o_current_name rangeOfString:[o_search_field
1311 stringValue] options:NSCaseInsensitiveSearch ].length ||
1312 [o_current_author rangeOfString:[o_search_field
1313 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1315 vlc_object_release( p_playlist );
1316 /*Adds the parent items in the result array as well, so that we can
1318 return [NSMutableArray arrayWithObject: [NSValue
1319 valueWithPointer: p_item->pp_children[i_current]]];
1321 if( p_item->pp_children[i_current]->i_children > 0 )
1323 id o_result = [self subSearchItem:
1324 p_item->pp_children[i_current]];
1325 if( o_result != NULL )
1327 vlc_object_release( p_playlist );
1328 [o_result insertObject: [NSValue valueWithPointer:
1329 p_item->pp_children[i_current]] atIndex:0];
1334 vlc_object_release( p_playlist );
1338 - (IBAction)searchItem:(id)sender
1340 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1342 playlist_view_t * p_view;
1348 b_selected_item_met = NO;
1350 if( p_playlist == NULL )
1352 p_view = playlist_ViewFind( p_playlist, i_current_view );
1356 /*First, only search after the selected item:*
1357 *(b_selected_item_met = NO) */
1358 o_result = [self subSearchItem:p_view->p_root];
1359 if( o_result == NULL )
1361 /* If the first search failed, search again from the beginning */
1362 o_result = [self subSearchItem:p_view->p_root];
1364 if( o_result != NULL )
1367 if( [[o_result objectAtIndex: 0] pointerValue] ==
1368 p_playlist->p_general )
1373 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1375 [o_outline_view expandItem: [o_outline_dict objectForKey:
1376 [NSString stringWithFormat: @"%p",
1377 [[o_result objectAtIndex: i] pointerValue]]]];
1379 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1380 [NSString stringWithFormat: @"%p",
1381 [[o_result objectAtIndex: [o_result count] - 1 ]
1386 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1387 [o_outline_view scrollRowToVisible: i_row];
1390 vlc_object_release( p_playlist );
1393 - (IBAction)recursiveExpandNode:(id)sender
1396 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1397 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1399 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1400 isItemExpandable: o_item] )
1402 for( i = 0 ; i < p_item->i_parents ; i++ )
1404 if( p_item->pp_parents[i]->i_view == i_current_view )
1406 o_item = [o_outline_dict objectForKey: [NSString
1407 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1413 /* We need to collapse the node first, since OSX refuses to recursively
1414 expand an already expanded node, even if children nodes are collapsed. */
1415 [o_outline_view collapseItem: o_item collapseChildren: YES];
1416 [o_outline_view expandItem: o_item expandChildren: YES];
1419 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1423 vlc_bool_t b_item_sel;
1425 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1427 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1428 [o_outline_view selectedRow] != -1 );
1429 b_rows = [o_outline_view numberOfRows] != 0;
1431 [o_mi_play setEnabled: b_item_sel];
1432 [o_mi_delete setEnabled: b_item_sel];
1433 [o_mi_selectall setEnabled: b_rows];
1434 [o_mi_info setEnabled: b_item_sel];
1435 [o_mi_preparse setEnabled: b_item_sel];
1436 [o_mi_recursive_expand setEnabled: b_item_sel];
1437 [o_mi_sort_name setEnabled: b_item_sel];
1438 [o_mi_sort_author setEnabled: b_item_sel];
1440 return( o_ctx_menu );
1443 - (void)outlineView: (NSTableView*)o_tv
1444 didClickTableColumn:(NSTableColumn *)o_tc
1446 int i_mode = 0, i_type;
1447 intf_thread_t *p_intf = VLCIntf;
1448 playlist_view_t *p_view;
1450 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1452 if( p_playlist == NULL )
1457 /* Check whether the selected table column header corresponds to a
1458 sortable table column*/
1459 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1461 vlc_object_release( p_playlist );
1465 p_view = playlist_ViewFind( p_playlist, i_current_view );
1467 if( o_tc_sortColumn == o_tc )
1469 b_isSortDescending = !b_isSortDescending;
1473 b_isSortDescending = VLC_FALSE;
1476 if( o_tc == o_tc_name )
1478 i_mode = SORT_TITLE;
1480 else if( o_tc == o_tc_author )
1482 i_mode = SORT_AUTHOR;
1485 if( b_isSortDescending )
1487 i_type = ORDER_REVERSE;
1491 i_type = ORDER_NORMAL;
1494 vlc_mutex_lock( &p_playlist->object_lock );
1495 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1496 vlc_mutex_unlock( &p_playlist->object_lock );
1498 vlc_object_release( p_playlist );
1499 [self playlistUpdated];
1501 o_tc_sortColumn = o_tc;
1502 [o_outline_view setHighlightedTableColumn:o_tc];
1504 if( b_isSortDescending )
1506 [o_outline_view setIndicatorImage:o_descendingSortingImage
1507 inTableColumn:o_tc];
1511 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1512 inTableColumn:o_tc];
1517 - (void)outlineView:(NSOutlineView *)outlineView
1518 willDisplayCell:(id)cell
1519 forTableColumn:(NSTableColumn *)tableColumn
1522 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1527 if( !p_playlist ) return;
1529 o_playing_item = [o_outline_dict objectForKey:
1530 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1532 if( [self isItem: [o_playing_item pointerValue] inNode:
1533 [item pointerValue] checkItemExistence: YES]
1534 || [o_playing_item isEqual: item] )
1536 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1540 [cell setFont: [NSFont systemFontOfSize: 0]];
1542 vlc_object_release( p_playlist );
1545 - (IBAction)addNode:(id)sender
1547 /* simply adds a new node to the end of the playlist */
1548 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1555 playlist_item_t * p_item = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
1556 _("Empty Folder"), p_playlist->p_general );
1559 msg_Warn( VLCIntf, "node creation failed" );
1561 playlist_ViewUpdate( p_playlist, VIEW_CATEGORY );
1563 vlc_object_release( p_playlist );
1568 @implementation VLCPlaylist (NSOutlineViewDataSource)
1570 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1572 id o_value = [super outlineView: outlineView child: index ofItem: item];
1573 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1576 if( !p_playlist ) return nil;
1578 if( p_playlist->i_size >= 2 )
1580 [o_status_field setStringValue: [NSString stringWithFormat:
1581 _NS("%i items in the playlist"), p_playlist->i_size]];
1585 if( p_playlist->i_size == 0 )
1587 [o_status_field setStringValue: _NS("No items in the playlist")];
1591 [o_status_field setStringValue: _NS("1 item in the playlist")];
1594 vlc_object_release( p_playlist );
1596 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1597 [o_value pointerValue]]];
1603 /* Required for drag & drop and reordering */
1604 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1607 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1610 /* First remove the items that were moved during the last drag & drop
1612 [o_items_array removeAllObjects];
1613 [o_nodes_array removeAllObjects];
1615 if( !p_playlist ) return NO;
1617 for( i = 0 ; i < [items count] ; i++ )
1619 id o_item = [items objectAtIndex: i];
1621 /* Refuse to move items that are not in the General Node
1622 (Service Discovery) */
1623 if( ![self isItem: [o_item pointerValue] inNode:
1624 p_playlist->p_general checkItemExistence: NO])
1626 vlc_object_release(p_playlist);
1629 /* Fill the items and nodes to move in 2 different arrays */
1630 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1631 [o_nodes_array addObject: o_item];
1633 [o_items_array addObject: o_item];
1636 /* Now we need to check if there are selected items that are in already
1637 selected nodes. In that case, we only want to move the nodes */
1638 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1639 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1641 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1642 a Drop operation coming from the playlist. */
1644 [pboard declareTypes: [NSArray arrayWithObjects:
1645 @"VLCPlaylistItemPboardType", nil] owner: self];
1646 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1648 vlc_object_release(p_playlist);
1652 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1654 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1656 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1658 if( !p_playlist ) return NSDragOperationNone;
1660 /* Dropping ON items is not allowed if item is not a node */
1663 if( index == NSOutlineViewDropOnItemIndex &&
1664 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1666 vlc_object_release( p_playlist );
1667 return NSDragOperationNone;
1671 /* We refuse to drop an item in anything else than a child of the General
1672 Node. We still accept items that would be root nodes of the outlineview
1673 however, to allow drop in an empty playlist. */
1674 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1675 checkItemExistence: NO] || item == nil) )
1677 vlc_object_release( p_playlist );
1678 return NSDragOperationNone;
1681 /* Drop from the Playlist */
1682 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1685 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1687 /* We refuse to Drop in a child of an item we are moving */
1688 if( [self isItem: [item pointerValue] inNode:
1689 [[o_nodes_array objectAtIndex: i] pointerValue]
1690 checkItemExistence: NO] )
1692 vlc_object_release( p_playlist );
1693 return NSDragOperationNone;
1696 vlc_object_release(p_playlist);
1697 return NSDragOperationMove;
1700 /* Drop from the Finder */
1701 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1703 vlc_object_release(p_playlist);
1704 return NSDragOperationGeneric;
1706 vlc_object_release(p_playlist);
1707 return NSDragOperationNone;
1710 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1712 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1714 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1716 if( !p_playlist ) return NO;
1718 /* Drag & Drop inside the playlist */
1719 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1721 int i_row, i_removed_from_node = 0;
1723 playlist_item_t *p_new_parent, *p_item = NULL;
1724 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1726 /* If the item is to be dropped as root item of the outline, make it a
1727 child of the General node.
1728 Else, choose the proposed parent as parent. */
1729 if( item == nil ) p_new_parent = p_playlist->p_general;
1730 else p_new_parent = [item pointerValue];
1732 /* Make sure the proposed parent is a node.
1733 (This should never be true) */
1734 if( p_new_parent->i_children < 0 )
1736 vlc_object_release( p_playlist );
1740 for( i = 0; i < [o_all_items count]; i++ )
1742 playlist_item_t *p_old_parent = NULL;
1743 int i_old_index = 0;
1745 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1746 p_old_parent = [self parentOfItem: p_item];
1749 /* We may need the old index later */
1750 if( p_new_parent == p_old_parent )
1753 for( j = 0; j < p_old_parent->i_children; j++ )
1755 if( p_old_parent->pp_children[j] == p_item )
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 */
1778 if ((p_new_parent == p_old_parent &&
1779 i_old_index < index + (int)i) )
1781 i_removed_from_node++;
1783 i_new_index = index + i - i_removed_from_node;
1785 // Reattach the item to the new position
1786 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1787 p_new_parent, i_new_index );
1789 vlc_mutex_unlock( &p_playlist->object_lock );
1791 [self playlistUpdated];
1792 i_row = [o_outline_view rowForItem:[o_outline_dict
1793 objectForKey:[NSString stringWithFormat: @"%p",
1794 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1798 i_row = [o_outline_view rowForItem:[o_outline_dict
1799 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1802 [o_outline_view deselectAll: self];
1803 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1804 [o_outline_view scrollRowToVisible: i_row];
1806 vlc_object_release(p_playlist);
1810 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1813 playlist_item_t *p_node = [item pointerValue];
1815 NSArray *o_array = [NSArray array];
1816 NSArray *o_values = [[o_pasteboard propertyListForType:
1817 NSFilenamesPboardType]
1818 sortedArrayUsingSelector:
1819 @selector(caseInsensitiveCompare:)];
1821 for( i = 0; i < (int)[o_values count]; i++)
1823 NSDictionary *o_dic;
1824 o_dic = [NSDictionary dictionaryWithObject:[o_values
1825 objectAtIndex:i] forKey:@"ITEM_URL"];
1826 o_array = [o_array arrayByAddingObject: o_dic];
1831 [self appendArray: o_array atPos: index enqueue: YES];
1833 /* This should never occur */
1834 else if( p_node->i_children == -1 )
1836 vlc_object_release( p_playlist );
1841 [self appendNodeArray: o_array inNode: p_node
1842 atPos: index inView: i_current_view enqueue: YES];
1844 vlc_object_release( p_playlist );
1847 vlc_object_release( p_playlist );
1851 /* Delegate method of NSWindow */
1852 /*- (void)windowWillClose:(NSNotification *)aNotification
1854 [o_btn_playlist setState: NSOffState];