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"
53 #import <vlc_interface.h>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
111 o_outline_dict = [[NSMutableDictionary alloc] init];
117 playlist_t * p_playlist = pl_Yield( VLCIntf );
118 [o_outline_view setTarget: self];
119 [o_outline_view setDelegate: self];
120 [o_outline_view setDataSource: self];
122 vlc_object_release( p_playlist );
128 [[o_tc_name headerCell] setStringValue:_NS("Name")];
129 [[o_tc_author headerCell] setStringValue:_NS("Author")];
130 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
133 - (NSOutlineView *)outlineView
135 return o_outline_view;
138 - (playlist_item_t *)selectedPlaylistItem
140 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
146 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
148 /* return the number of children for Obj-C pointer item */ /* DONE */
149 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
152 playlist_item_t *p_item = NULL;
153 playlist_t * p_playlist = pl_Yield( VLCIntf );
154 if( outlineView != o_outline_view )
156 vlc_object_release( p_playlist );
163 p_item = p_playlist->p_root_category;
167 p_item = (playlist_item_t *)[item pointerValue];
170 i_return = p_item->i_children;
171 vlc_object_release( p_playlist );
179 /* return the child at index for the Obj-C pointer item */ /* DONE */
180 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
182 playlist_item_t *p_return = NULL, *p_item = NULL;
184 playlist_t * p_playlist = pl_Yield( VLCIntf );
189 p_item = p_playlist->p_root_category;
193 p_item = (playlist_item_t *)[item pointerValue];
195 if( p_item && index < p_item->i_children && index >= 0 )
196 p_return = p_item->pp_children[index];
198 vlc_object_release( p_playlist );
200 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
204 o_value = [[NSValue valueWithPointer: p_return] retain];
205 msg_Err( VLCIntf, "missing playlist item's pointer value" );
210 /* is the item expandable */
211 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
214 playlist_t *p_playlist = pl_Yield( VLCIntf );
219 if( p_playlist->p_root_category )
221 i_return = p_playlist->p_root_category->i_children;
226 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
228 i_return = p_item->i_children;
230 vlc_object_release( p_playlist );
232 return (i_return > 0);
235 /* retrieve the string values for the cells */
236 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
239 playlist_item_t *p_item;
241 /* For error handling */
242 static BOOL attempted_reload = NO;
244 if( item == nil || ![item isKindOfClass: [NSValue class]] )
246 /* Attempt to fix the error by asking for a data redisplay
247 * This might cause infinite loop, so add a small check */
248 if( !attempted_reload )
250 attempted_reload = YES;
251 [outlineView reloadData];
256 p_item = (playlist_item_t *)[item pointerValue];
257 if( !p_item || !p_item->p_input )
259 /* Attempt to fix the error by asking for a data redisplay
260 * This might cause infinite loop, so add a small check */
261 if( !attempted_reload )
263 attempted_reload = YES;
264 [outlineView reloadData];
269 attempted_reload = NO;
271 if( [[o_tc identifier] isEqualToString:@"1"] )
273 /* sanity check to prevent the NSString class from crashing */
274 if( p_item->p_input->p_meta && p_item->p_input->p_meta->psz_title &&
275 *p_item->p_input->p_meta->psz_title )
277 o_value = [NSString stringWithUTF8String:
278 p_item->p_input->p_meta->psz_title];
279 if( o_value == NULL )
280 o_value = [NSString stringWithCString:
281 p_item->p_input->p_meta->psz_title];
283 else if( p_item->p_input->psz_name != NULL )
285 o_value = [NSString stringWithUTF8String:
286 p_item->p_input->psz_name];
287 if( o_value == NULL )
288 o_value = [NSString stringWithCString:
289 p_item->p_input->psz_name];
292 else if( [[o_tc identifier] isEqualToString:@"2"] && p_item->p_input->p_meta &&
293 p_item->p_input->p_meta->psz_artist && *p_item->p_input->p_meta->psz_artist )
295 o_value = [NSString stringWithUTF8String:
296 p_item->p_input->p_meta->psz_artist];
297 if( o_value == NULL )
298 o_value = [NSString stringWithCString:
299 p_item->p_input->p_meta->psz_artist];
301 else if( [[o_tc identifier] isEqualToString:@"3"] )
303 char psz_duration[MSTRTIME_MAX_SIZE];
304 mtime_t dur = p_item->p_input->i_duration;
307 secstotimestr( psz_duration, dur/1000000 );
308 o_value = [NSString stringWithUTF8String: psz_duration];
312 o_value = @"-:--:--";
321 /*****************************************************************************
322 * VLCPlaylistWizard implementation
323 *****************************************************************************/
324 @implementation VLCPlaylistWizard
326 - (IBAction)reloadOutlineView
328 /* Only reload the outlineview if the wizard window is open since this can
329 be quite long on big playlists */
330 if( [[o_outline_view window] isVisible] )
332 [o_outline_view reloadData];
338 /*****************************************************************************
339 * extension to NSOutlineView's interface to fix compilation warnings
340 * and let us access these 2 functions properly
341 * this uses a private Apple-API, but works fine on all current OSX releases
342 * keep checking for compatiblity with future releases though
343 *****************************************************************************/
345 @interface NSOutlineView (UndocumentedSortImages)
346 + (NSImage *)_defaultTableHeaderSortImage;
347 + (NSImage *)_defaultTableHeaderReverseSortImage;
351 /*****************************************************************************
352 * VLCPlaylist implementation
353 *****************************************************************************/
354 @implementation VLCPlaylist
361 o_nodes_array = [[NSMutableArray alloc] init];
362 o_items_array = [[NSMutableArray alloc] init];
369 playlist_t * p_playlist = pl_Yield( VLCIntf );
370 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
375 [super awakeFromNib];
377 [o_outline_view setDoubleAction: @selector(playItem:)];
379 [o_outline_view registerForDraggedTypes:
380 [NSArray arrayWithObjects: NSFilenamesPboardType,
381 @"VLCPlaylistItemPboardType", nil]];
382 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
384 /* this uses private Apple API which works fine until 10.4,
385 * but keep checking in the future!
386 * These methods are being added artificially to NSOutlineView's interface above */
387 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
388 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
390 o_tc_sortColumn = nil;
392 for( i_index = 0; i_index < p_list->i_count; i_index++ )
394 vlc_bool_t b_enabled;
397 module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object ;
399 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
401 /* Check for submodules */
403 while( p_parser->pp_shortcuts[++i] != NULL ); i--;
405 /* Check whether to enable these menuitems */
406 objectname = i>=0 ? (char *)p_parser->pp_shortcuts[i] : (char *)p_parser->psz_object_name;
407 b_enabled = playlist_IsServicesDiscoveryLoaded( p_playlist, objectname );
409 /* Create the menu entries used in the playlist menu */
410 o_lmi = [[o_mi_services submenu] addItemWithTitle:
411 [NSString stringWithUTF8String:
412 p_parser->psz_longname ? p_parser->psz_longname :
413 ( p_parser->psz_shortname ? p_parser->psz_shortname:
415 action: @selector(servicesChange:)
417 [o_lmi setTarget: self];
418 [o_lmi setRepresentedObject: [NSString stringWithCString: objectname]];
419 if( b_enabled ) [o_lmi setState: NSOnState];
421 /* Create the menu entries for the main menu */
422 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
423 [NSString stringWithUTF8String:
424 p_parser->psz_longname ? p_parser->psz_longname :
425 ( p_parser->psz_shortname ? p_parser->psz_shortname:
427 action: @selector(servicesChange:)
429 [o_lmi setTarget: self];
430 [o_lmi setRepresentedObject: [NSString stringWithCString:objectname]];
431 if( b_enabled ) [o_lmi setState: NSOnState];
434 vlc_list_release( p_list );
435 vlc_object_release( p_playlist );
437 //[self playlistUpdated];
440 - (void)searchfieldChanged:(NSNotification *)o_notification
442 [o_search_field setStringValue:[[o_notification object] stringValue]];
449 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
450 [o_mi_play setTitle: _NS("Play")];
451 [o_mi_delete setTitle: _NS("Delete")];
452 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
453 [o_mi_selectall setTitle: _NS("Select All")];
454 [o_mi_info setTitle: _NS("Information")];
455 [o_mi_preparse setTitle: _NS("Get Stream Information")];
456 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
457 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
458 [o_mi_services setTitle: _NS("Services discovery")];
459 [o_status_field setStringValue: [NSString stringWithFormat:
460 _NS("No items in the playlist")]];
463 [o_search_button setTitle: _NS("Search")];
465 [o_search_field setToolTip: _NS("Search in Playlist")];
466 [o_mi_addNode setTitle: _NS("Add Folder to Playlist")];
468 [o_save_accessory_text setStringValue: _NS("File Format:")];
469 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
470 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
473 - (void)playlistUpdated
477 /* Clear indications of any existing column sorting */
478 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
480 [o_outline_view setIndicatorImage:nil inTableColumn:
481 [[o_outline_view tableColumns] objectAtIndex:i]];
484 [o_outline_view setHighlightedTableColumn:nil];
485 o_tc_sortColumn = nil;
486 // TODO Find a way to keep the dict size to a minimum
487 //[o_outline_dict removeAllObjects];
488 [o_outline_view reloadData];
489 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
490 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
492 playlist_t *p_playlist = pl_Yield( VLCIntf );
494 if( playlist_CurrentSize( p_playlist ) >= 2 )
496 [o_status_field setStringValue: [NSString stringWithFormat:
497 _NS("%i items in the playlist"),
498 playlist_CurrentSize( p_playlist )]];
502 if( playlist_IsEmpty( p_playlist ) )
503 [o_status_field setStringValue: _NS("No items in the playlist")];
505 [o_status_field setStringValue: _NS("1 item in the playlist")];
507 vlc_object_release( p_playlist );
510 - (void)playModeUpdated
512 playlist_t *p_playlist = pl_Yield( VLCIntf );
513 vlc_value_t val, val2;
515 var_Get( p_playlist, "loop", &val2 );
516 var_Get( p_playlist, "repeat", &val );
517 if( val.b_bool == VLC_TRUE )
519 [[[VLCMain sharedInstance] getControls] repeatOne];
521 else if( val2.b_bool == VLC_TRUE )
523 [[[VLCMain sharedInstance] getControls] repeatAll];
527 [[[VLCMain sharedInstance] getControls] repeatOff];
530 [[[VLCMain sharedInstance] getControls] shuffle];
532 vlc_object_release( p_playlist );
535 - (void)updateRowSelection
540 playlist_t *p_playlist = pl_Yield( VLCIntf );
541 playlist_item_t *p_item, *p_temp_item;
542 NSMutableArray *o_array = [NSMutableArray array];
544 p_item = p_playlist->status.p_item;
547 vlc_object_release(p_playlist);
551 p_temp_item = p_item;
552 while( p_temp_item->p_parent )
554 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
555 p_temp_item = p_temp_item->p_parent;
556 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
558 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
560 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
566 for( j = 0; j < [o_array count] - 1; j++ )
569 if( ( o_item = [o_outline_dict objectForKey:
570 [NSString stringWithFormat: @"%p",
571 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
573 [o_outline_view expandItem: o_item];
578 i_row = [o_outline_view rowForItem:[o_outline_dict
579 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
581 [o_outline_view selectRow: i_row byExtendingSelection: NO];
582 [o_outline_view scrollRowToVisible: i_row];
584 vlc_object_release( p_playlist );
586 /* update our info-panel to reflect the new item */
587 [[[VLCMain sharedInstance] getInfo] updatePanel];
590 /* Check if p_item is a child of p_node recursively. We need to check the item
591 existence first since OSX sometimes tries to redraw items that have been
592 deleted. We don't do it when not required since this verification takes
593 quite a long time on big playlists (yes, pretty hacky). */
594 - (BOOL)isItem: (playlist_item_t *)p_item
595 inNode: (playlist_item_t *)p_node
596 checkItemExistence:(BOOL)b_check
599 playlist_t * p_playlist = pl_Yield( VLCIntf );
600 playlist_item_t *p_temp_item = p_item;
602 if( p_node == p_item )
604 vlc_object_release(p_playlist);
608 if( p_node->i_children < 1)
610 vlc_object_release(p_playlist);
617 vlc_mutex_lock( &p_playlist->object_lock );
621 /* Since outlineView: willDisplayCell:... may call this function with
622 p_items that don't exist anymore, first check if the item is still
623 in the playlist. Any cleaner solution welcomed. */
624 for( i = 0; i < p_playlist->all_items.i_size; i++ )
626 if( ARRAY_VAL( p_playlist->all_items, i) == p_item ) break;
627 else if ( i == p_playlist->all_items.i_size - 1 )
629 vlc_object_release( p_playlist );
630 vlc_mutex_unlock( &p_playlist->object_lock );
638 p_temp_item = p_temp_item->p_parent;
639 if( p_temp_item == p_node )
641 vlc_mutex_unlock( &p_playlist->object_lock );
642 vlc_object_release( p_playlist );
646 vlc_mutex_unlock( &p_playlist->object_lock );
649 vlc_object_release( p_playlist );
653 /* This method is usefull for instance to remove the selected children of an
654 already selected node */
655 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
658 for( i = 0 ; i < [o_items count] ; i++ )
660 for ( j = 0 ; j < [o_nodes count] ; j++ )
662 if( o_items == o_nodes)
664 if( j == i ) continue;
666 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
667 inNode: [[o_nodes objectAtIndex:j] pointerValue]
668 checkItemExistence: NO] )
670 [o_items removeObjectAtIndex:i];
671 /* We need to execute the next iteration with the same index
672 since the current item has been deleted */
681 - (IBAction)savePlaylist:(id)sender
683 intf_thread_t * p_intf = VLCIntf;
684 playlist_t * p_playlist = pl_Yield( p_intf );
686 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
687 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
689 //[o_save_panel setAllowedFileTypes: [NSArray arrayWithObjects: @"m3u", @"xpf", nil] ];
690 [o_save_panel setTitle: _NS("Save Playlist")];
691 [o_save_panel setPrompt: _NS("Save")];
692 [o_save_panel setAccessoryView: o_save_accessory_view];
694 if( [o_save_panel runModalForDirectory: nil
695 file: o_name] == NSOKButton )
697 NSString *o_filename = [o_save_panel filename];
699 if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
701 NSString * o_real_filename;
703 range.location = [o_filename length] - [@".xspf" length];
704 range.length = [@".xspf" length];
706 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
707 range: range] != NSOrderedSame )
709 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
713 o_real_filename = o_filename;
715 playlist_Export( p_playlist,
716 [o_real_filename fileSystemRepresentation],
717 p_playlist->p_local_category, "export-xspf" );
721 NSString * o_real_filename;
723 range.location = [o_filename length] - [@".m3u" length];
724 range.length = [@".m3u" length];
726 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
727 range: range] != NSOrderedSame )
729 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
733 o_real_filename = o_filename;
735 playlist_Export( p_playlist,
736 [o_real_filename fileSystemRepresentation],
737 p_playlist->p_local_category, "export-m3u" );
740 vlc_object_release( p_playlist );
743 /* When called retrieves the selected outlineview row and plays that node or item */
744 - (IBAction)playItem:(id)sender
746 intf_thread_t * p_intf = VLCIntf;
747 playlist_t * p_playlist = pl_Yield( p_intf );
749 playlist_item_t *p_item;
750 playlist_item_t *p_node = NULL;
752 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
756 if( p_item->i_children == -1 )
758 p_node = p_item->p_parent;
764 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
766 p_item = p_node->pp_children[0];
773 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, p_node, p_item );
775 vlc_object_release( p_playlist );
778 /* When called retrieves the selected outlineview row and plays that node or item */
779 - (IBAction)preparseItem:(id)sender
782 NSMutableArray *o_to_preparse;
783 intf_thread_t * p_intf = VLCIntf;
784 playlist_t * p_playlist = pl_Yield( p_intf );
786 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
787 i_count = [o_to_preparse count];
791 playlist_item_t *p_item = NULL;
793 for( i = 0; i < i_count; i++ )
795 o_number = [o_to_preparse lastObject];
796 i_row = [o_number intValue];
797 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
798 [o_to_preparse removeObject: o_number];
799 [o_outline_view deselectRow: i_row];
803 if( p_item->i_children == -1 )
805 playlist_PreparseEnqueue( p_playlist, p_item->p_input );
809 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
813 vlc_object_release( p_playlist );
814 [self playlistUpdated];
817 - (IBAction)servicesChange:(id)sender
819 NSMenuItem *o_mi = (NSMenuItem *)sender;
820 NSString *o_string = [o_mi representedObject];
821 playlist_t * p_playlist = pl_Yield( VLCIntf );
822 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
823 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
825 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
827 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
828 [o_string cString] ) ? YES : NO];
830 vlc_object_release( p_playlist );
831 [self playlistUpdated];
835 - (IBAction)selectAll:(id)sender
837 [o_outline_view selectAll: nil];
840 - (IBAction)deleteItem:(id)sender
842 int i, i_count, i_row;
843 NSMutableArray *o_to_delete;
846 playlist_t * p_playlist;
847 intf_thread_t * p_intf = VLCIntf;
849 p_playlist = pl_Yield( p_intf );
851 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
852 i_count = [o_to_delete count];
854 for( i = 0; i < i_count; i++ )
856 o_number = [o_to_delete lastObject];
857 i_row = [o_number intValue];
858 id o_item = [o_outline_view itemAtRow: i_row];
859 playlist_item_t *p_item = [o_item pointerValue];
860 [o_to_delete removeObject: o_number];
861 [o_outline_view deselectRow: i_row];
863 if( [[o_outline_view dataSource] outlineView:o_outline_view
864 numberOfChildrenOfItem: o_item] > 0 )
865 //is a node and not an item
867 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
868 [self isItem: p_playlist->status.p_item inNode:
869 ((playlist_item_t *)[o_item pointerValue])
870 checkItemExistence: NO] == YES )
872 // if current item is in selected node and is playing then stop playlist
873 playlist_Stop( p_playlist );
875 vlc_mutex_lock( &p_playlist->object_lock );
876 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
877 vlc_mutex_unlock( &p_playlist->object_lock );
881 playlist_DeleteFromInput( p_playlist, p_item->p_input->i_id, VLC_FALSE );
884 [self playlistUpdated];
885 vlc_object_release( p_playlist );
888 - (IBAction)sortNodeByName:(id)sender
890 [self sortNode: SORT_TITLE];
893 - (IBAction)sortNodeByAuthor:(id)sender
895 [self sortNode: SORT_ARTIST];
898 - (void)sortNode:(int)i_mode
900 playlist_t * p_playlist = pl_Yield( VLCIntf );
901 playlist_item_t * p_item;
903 if( [o_outline_view selectedRow] > -1 )
905 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
909 /*If no item is selected, sort the whole playlist*/
911 p_item = p_playlist->p_root_category;
914 if( p_item->i_children > -1 ) // the item is a node
916 vlc_mutex_lock( &p_playlist->object_lock );
917 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
918 vlc_mutex_unlock( &p_playlist->object_lock );
922 vlc_mutex_lock( &p_playlist->object_lock );
923 playlist_RecursiveNodeSort( p_playlist,
924 p_item->p_parent, i_mode, ORDER_NORMAL );
925 vlc_mutex_unlock( &p_playlist->object_lock );
927 vlc_object_release( p_playlist );
928 [self playlistUpdated];
931 - (input_item_t *)createItem:(NSDictionary *)o_one_item
933 intf_thread_t * p_intf = VLCIntf;
934 playlist_t * p_playlist = pl_Yield( p_intf );
936 input_item_t *p_input;
938 BOOL b_rem = FALSE, b_dir = FALSE;
939 NSString *o_uri, *o_name;
944 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
945 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
946 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
948 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
949 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
951 int i_count, i_index;
952 struct statfs *mounts = NULL;
954 i_count = getmntinfo (&mounts, MNT_NOWAIT);
955 /* getmntinfo returns a pointer to static data. Do not free. */
956 for( i_index = 0 ; i_index < i_count; i_index++ )
958 NSMutableString *o_temp, *o_temp2;
959 o_temp = [NSMutableString stringWithString: o_uri];
960 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
961 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
962 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
963 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
965 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
967 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
971 /* If no name, then make a guess */
972 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
974 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
975 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
976 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
978 /* All of this is to make sure CD's play when you D&D them on VLC */
979 /* Converts mountpoint to a /dev file */
982 NSMutableString *o_temp;
984 buf = (struct statfs *) malloc (sizeof(struct statfs));
985 statfs( [o_uri fileSystemRepresentation], buf );
986 psz_dev = strdup(buf->f_mntfromname);
987 o_temp = [NSMutableString stringWithCString: psz_dev ];
988 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
989 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
990 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
994 p_input = input_ItemNew( p_playlist, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1000 for( i = 0; i < (int)[o_options count]; i++ )
1002 input_ItemAddOption( p_input, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1006 /* Recent documents menu */
1007 o_true_file = [NSURL fileURLWithPath: o_uri];
1008 if( o_true_file != nil )
1010 [[NSDocumentController sharedDocumentController]
1011 noteNewRecentDocumentURL: o_true_file];
1014 vlc_object_release( p_playlist );
1018 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1021 playlist_t * p_playlist = pl_Yield( VLCIntf );
1023 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1025 input_item_t *p_input;
1026 NSDictionary *o_one_item;
1029 o_one_item = [o_array objectAtIndex: i_item];
1030 p_input = [self createItem: o_one_item];
1037 playlist_AddInput( p_playlist, p_input, PLAYLIST_INSERT,
1038 i_position == -1 ? PLAYLIST_END : i_position + i_item, VLC_TRUE,
1041 if( i_item == 0 && !b_enqueue )
1043 playlist_item_t *p_item;
1044 p_item = playlist_ItemGetByInput( p_playlist, p_input, VLC_TRUE );
1045 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, NULL, p_item );
1049 playlist_item_t *p_item;
1050 p_item = playlist_ItemGetByInput( p_playlist, p_input, VLC_TRUE );
1051 playlist_Control( p_playlist, PLAYLIST_PREPARSE, VLC_TRUE, p_item );
1054 [self playlistUpdated];
1055 vlc_object_release( p_playlist );
1058 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position enqueue:(BOOL)b_enqueue
1061 playlist_t * p_playlist = pl_Yield( VLCIntf );
1063 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1065 input_item_t *p_input;
1066 NSDictionary *o_one_item;
1069 o_one_item = [o_array objectAtIndex: i_item];
1070 p_input = [self createItem: o_one_item];
1077 playlist_NodeAddInput( p_playlist, p_input, p_node,
1080 PLAYLIST_END : i_position + i_item, VLC_FALSE );
1083 if( i_item == 0 && !b_enqueue )
1085 playlist_item_t *p_item;
1086 p_item = playlist_ItemGetByInput( p_playlist, p_input, VLC_TRUE );
1087 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, NULL, p_item );
1091 playlist_item_t *p_item;
1092 p_item = playlist_ItemGetByInput( p_playlist, p_input, VLC_TRUE );
1093 playlist_Control( p_playlist, PLAYLIST_PREPARSE, VLC_TRUE, p_item );
1096 [self playlistUpdated];
1097 vlc_object_release( p_playlist );
1100 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1102 playlist_t *p_playlist = pl_Yield( VLCIntf );
1103 playlist_item_t *p_selected_item;
1104 int i_current, i_selected_row;
1106 i_selected_row = [o_outline_view selectedRow];
1107 if (i_selected_row < 0)
1110 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1111 i_selected_row] pointerValue];
1113 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1116 NSString *o_current_name, *o_current_author;
1118 vlc_mutex_lock( &p_playlist->object_lock );
1119 o_current_name = [NSString stringWithUTF8String:
1120 p_item->pp_children[i_current]->p_input->psz_name];
1121 psz_temp = input_ItemGetInfo( p_item->p_input ,
1122 _("Meta-information"),_("Artist") );
1123 o_current_author = [NSString stringWithUTF8String: psz_temp];
1125 vlc_mutex_unlock( &p_playlist->object_lock );
1127 if( p_selected_item == p_item->pp_children[i_current] &&
1128 b_selected_item_met == NO )
1130 b_selected_item_met = YES;
1132 else if( p_selected_item == p_item->pp_children[i_current] &&
1133 b_selected_item_met == YES )
1135 vlc_object_release( p_playlist );
1138 else if( b_selected_item_met == YES &&
1139 ( [o_current_name rangeOfString:[o_search_field
1140 stringValue] options:NSCaseInsensitiveSearch ].length ||
1141 [o_current_author rangeOfString:[o_search_field
1142 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1144 vlc_object_release( p_playlist );
1145 /*Adds the parent items in the result array as well, so that we can
1147 return [NSMutableArray arrayWithObject: [NSValue
1148 valueWithPointer: p_item->pp_children[i_current]]];
1150 if( p_item->pp_children[i_current]->i_children > 0 )
1152 id o_result = [self subSearchItem:
1153 p_item->pp_children[i_current]];
1154 if( o_result != NULL )
1156 vlc_object_release( p_playlist );
1157 [o_result insertObject: [NSValue valueWithPointer:
1158 p_item->pp_children[i_current]] atIndex:0];
1163 vlc_object_release( p_playlist );
1167 - (IBAction)searchItem:(id)sender
1169 playlist_t * p_playlist = pl_Yield( VLCIntf );
1175 b_selected_item_met = NO;
1177 /*First, only search after the selected item:*
1178 *(b_selected_item_met = NO) */
1179 o_result = [self subSearchItem:p_playlist->p_root_category];
1180 if( o_result == NULL )
1182 /* If the first search failed, search again from the beginning */
1183 o_result = [self subSearchItem:p_playlist->p_root_category];
1185 if( o_result != NULL )
1188 if( [[o_result objectAtIndex: 0] pointerValue] ==
1189 p_playlist->p_local_category )
1194 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1196 [o_outline_view expandItem: [o_outline_dict objectForKey:
1197 [NSString stringWithFormat: @"%p",
1198 [[o_result objectAtIndex: i] pointerValue]]]];
1200 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1201 [NSString stringWithFormat: @"%p",
1202 [[o_result objectAtIndex: [o_result count] - 1 ]
1207 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1208 [o_outline_view scrollRowToVisible: i_row];
1210 vlc_object_release( p_playlist );
1213 - (IBAction)recursiveExpandNode:(id)sender
1215 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1216 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1218 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1219 isItemExpandable: o_item] )
1221 o_item = [o_outline_dict objectForKey: [NSString
1222 stringWithFormat: @"%p", p_item->p_parent]];
1225 /* We need to collapse the node first, since OSX refuses to recursively
1226 expand an already expanded node, even if children nodes are collapsed. */
1227 [o_outline_view collapseItem: o_item collapseChildren: YES];
1228 [o_outline_view expandItem: o_item expandChildren: YES];
1231 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1235 vlc_bool_t b_item_sel;
1237 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1239 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1240 [o_outline_view selectedRow] != -1 );
1241 b_rows = [o_outline_view numberOfRows] != 0;
1243 [o_mi_play setEnabled: b_item_sel];
1244 [o_mi_delete setEnabled: b_item_sel];
1245 [o_mi_selectall setEnabled: b_rows];
1246 [o_mi_info setEnabled: b_item_sel];
1247 [o_mi_preparse setEnabled: b_item_sel];
1248 [o_mi_recursive_expand setEnabled: b_item_sel];
1249 [o_mi_sort_name setEnabled: b_item_sel];
1250 [o_mi_sort_author setEnabled: b_item_sel];
1252 return( o_ctx_menu );
1255 - (void)outlineView: (NSTableView*)o_tv
1256 didClickTableColumn:(NSTableColumn *)o_tc
1258 int i_mode = 0, i_type;
1259 intf_thread_t *p_intf = VLCIntf;
1261 playlist_t *p_playlist = pl_Yield( p_intf );
1263 /* Check whether the selected table column header corresponds to a
1264 sortable table column*/
1265 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1267 vlc_object_release( p_playlist );
1271 if( o_tc_sortColumn == o_tc )
1273 b_isSortDescending = !b_isSortDescending;
1277 b_isSortDescending = VLC_FALSE;
1280 if( o_tc == o_tc_name )
1282 i_mode = SORT_TITLE;
1284 else if( o_tc == o_tc_author )
1286 i_mode = SORT_ARTIST;
1289 if( b_isSortDescending )
1291 i_type = ORDER_REVERSE;
1295 i_type = ORDER_NORMAL;
1298 vlc_mutex_lock( &p_playlist->object_lock );
1299 playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_category, i_mode, i_type );
1300 vlc_mutex_unlock( &p_playlist->object_lock );
1302 vlc_object_release( p_playlist );
1303 [self playlistUpdated];
1305 o_tc_sortColumn = o_tc;
1306 [o_outline_view setHighlightedTableColumn:o_tc];
1308 if( b_isSortDescending )
1310 [o_outline_view setIndicatorImage:o_descendingSortingImage
1311 inTableColumn:o_tc];
1315 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1316 inTableColumn:o_tc];
1321 - (void)outlineView:(NSOutlineView *)outlineView
1322 willDisplayCell:(id)cell
1323 forTableColumn:(NSTableColumn *)tableColumn
1326 playlist_t *p_playlist = pl_Yield( VLCIntf );
1330 o_playing_item = [o_outline_dict objectForKey:
1331 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1333 if( [self isItem: [o_playing_item pointerValue] inNode:
1334 [item pointerValue] checkItemExistence: YES]
1335 || [o_playing_item isEqual: item] )
1337 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1341 [cell setFont: [NSFont systemFontOfSize: 0]];
1343 vlc_object_release( p_playlist );
1346 - (IBAction)addNode:(id)sender
1348 /* we have to create a new thread here because otherwise we would block the
1349 * interface since the interaction-stuff and this code would run in the same
1351 [NSThread detachNewThreadSelector: @selector(addNodeThreadedly)
1352 toTarget: self withObject:nil];
1353 [self playlistUpdated];
1356 - (void)addNodeThreadedly
1358 NSAutoreleasePool * ourPool = [[NSAutoreleasePool alloc] init];
1360 /* simply adds a new node to the end of the playlist */
1361 playlist_t * p_playlist = pl_Yield( VLCIntf );
1362 vlc_thread_set_priority( p_playlist, VLC_THREAD_PRIORITY_LOW );
1365 char *psz_name = NULL;
1366 playlist_item_t * p_item;
1367 ret_v = intf_UserStringInput( p_playlist, _("New Node"),
1368 _("Please enter a name for the new node."), &psz_name );
1370 if( psz_name != NULL && psz_name != "" )
1371 p_item = playlist_NodeCreate( p_playlist, psz_name,
1372 p_playlist->p_local_category, 0 );
1373 else if(! config_GetInt( p_playlist, "interact" ) )
1375 /* in case that the interaction is disabled, just give it a bogus name */
1376 p_item = playlist_NodeCreate( p_playlist, _("Empty Folder"),
1377 p_playlist->p_local_category, 0 );
1381 msg_Warn( VLCIntf, "node creation failed or cancelled by user" );
1383 vlc_object_release( p_playlist );
1389 @implementation VLCPlaylist (NSOutlineViewDataSource)
1391 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1393 id o_value = [super outlineView: outlineView child: index ofItem: item];
1394 playlist_t *p_playlist = pl_Yield( VLCIntf );
1396 if( playlist_CurrentSize( p_playlist ) >= 2 )
1398 [o_status_field setStringValue: [NSString stringWithFormat:
1399 _NS("%i items in the playlist"),
1400 playlist_CurrentSize( p_playlist )]];
1404 if( playlist_IsEmpty( p_playlist ) )
1406 [o_status_field setStringValue: _NS("No items in the playlist")];
1410 [o_status_field setStringValue: _NS("1 item in the playlist")];
1413 vlc_object_release( p_playlist );
1415 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1416 [o_value pointerValue]]];
1417 msg_Dbg( VLCIntf, "adding item %p", [o_value pointerValue] );
1422 /* Required for drag & drop and reordering */
1423 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1426 playlist_t *p_playlist = pl_Yield( VLCIntf );
1428 /* First remove the items that were moved during the last drag & drop
1430 [o_items_array removeAllObjects];
1431 [o_nodes_array removeAllObjects];
1433 for( i = 0 ; i < [items count] ; i++ )
1435 id o_item = [items objectAtIndex: i];
1437 /* Refuse to move items that are not in the General Node
1438 (Service Discovery) */
1439 if( ![self isItem: [o_item pointerValue] inNode:
1440 p_playlist->p_local_category checkItemExistence: NO])
1442 vlc_object_release(p_playlist);
1445 /* Fill the items and nodes to move in 2 different arrays */
1446 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1447 [o_nodes_array addObject: o_item];
1449 [o_items_array addObject: o_item];
1452 /* Now we need to check if there are selected items that are in already
1453 selected nodes. In that case, we only want to move the nodes */
1454 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1455 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1457 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1458 a Drop operation coming from the playlist. */
1460 [pboard declareTypes: [NSArray arrayWithObjects:
1461 @"VLCPlaylistItemPboardType", nil] owner: self];
1462 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1464 vlc_object_release(p_playlist);
1468 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1470 playlist_t *p_playlist = pl_Yield( VLCIntf );
1471 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1473 if( !p_playlist ) return NSDragOperationNone;
1475 /* Dropping ON items is not allowed if item is not a node */
1478 if( index == NSOutlineViewDropOnItemIndex &&
1479 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1481 vlc_object_release( p_playlist );
1482 return NSDragOperationNone;
1486 /* Don't allow on drop on playlist root element's child */
1487 if( !item && index != NSOutlineViewDropOnItemIndex)
1489 vlc_object_release( p_playlist );
1490 return NSDragOperationNone;
1493 /* We refuse to drop an item in anything else than a child of the General
1494 Node. We still accept items that would be root nodes of the outlineview
1495 however, to allow drop in an empty playlist. */
1496 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_local_category
1497 checkItemExistence: NO] || item == nil) )
1499 vlc_object_release( p_playlist );
1500 return NSDragOperationNone;
1503 /* Drop from the Playlist */
1504 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1507 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1509 /* We refuse to Drop in a child of an item we are moving */
1510 if( [self isItem: [item pointerValue] inNode:
1511 [[o_nodes_array objectAtIndex: i] pointerValue]
1512 checkItemExistence: NO] )
1514 vlc_object_release( p_playlist );
1515 return NSDragOperationNone;
1518 vlc_object_release( p_playlist );
1519 return NSDragOperationMove;
1522 /* Drop from the Finder */
1523 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1525 vlc_object_release( p_playlist );
1526 return NSDragOperationGeneric;
1528 vlc_object_release( p_playlist );
1529 return NSDragOperationNone;
1532 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1534 playlist_t * p_playlist = pl_Yield( VLCIntf );
1535 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1537 /* Drag & Drop inside the playlist */
1538 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1540 int i_row, i_removed_from_node = 0;
1542 playlist_item_t *p_new_parent, *p_item = NULL;
1543 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1545 /* If the item is to be dropped as root item of the outline, make it a
1546 child of the General node.
1547 Else, choose the proposed parent as parent. */
1548 if( item == nil ) p_new_parent = p_playlist->p_local_category;
1549 else p_new_parent = [item pointerValue];
1551 /* Make sure the proposed parent is a node.
1552 (This should never be true) */
1553 if( p_new_parent->i_children < 0 )
1555 vlc_object_release( p_playlist );
1559 for( i = 0; i < [o_all_items count]; i++ )
1561 playlist_item_t *p_old_parent = NULL;
1562 int i_old_index = 0;
1564 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1565 p_old_parent = p_item->p_parent;
1568 /* We may need the old index later */
1569 if( p_new_parent == p_old_parent )
1572 for( j = 0; j < p_old_parent->i_children; j++ )
1574 if( p_old_parent->pp_children[j] == p_item )
1582 vlc_mutex_lock( &p_playlist->object_lock );
1583 // Acually detach the item from the old position
1584 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1588 /* Calculate the new index */
1591 /* If we move the item in the same node, we need to take into
1592 account that one item will be deleted */
1595 if ((p_new_parent == p_old_parent &&
1596 i_old_index < index + (int)i) )
1598 i_removed_from_node++;
1600 i_new_index = index + i - i_removed_from_node;
1602 // Reattach the item to the new position
1603 playlist_NodeInsert( p_playlist, p_item, p_new_parent, i_new_index );
1605 vlc_mutex_unlock( &p_playlist->object_lock );
1607 [self playlistUpdated];
1608 i_row = [o_outline_view rowForItem:[o_outline_dict
1609 objectForKey:[NSString stringWithFormat: @"%p",
1610 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1614 i_row = [o_outline_view rowForItem:[o_outline_dict
1615 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1618 [o_outline_view deselectAll: self];
1619 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1620 [o_outline_view scrollRowToVisible: i_row];
1622 vlc_object_release( p_playlist );
1626 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1629 playlist_item_t *p_node = [item pointerValue];
1631 NSArray *o_array = [NSArray array];
1632 NSArray *o_values = [[o_pasteboard propertyListForType:
1633 NSFilenamesPboardType]
1634 sortedArrayUsingSelector:
1635 @selector(caseInsensitiveCompare:)];
1637 for( i = 0; i < (int)[o_values count]; i++)
1639 NSDictionary *o_dic;
1640 o_dic = [NSDictionary dictionaryWithObject:[o_values
1641 objectAtIndex:i] forKey:@"ITEM_URL"];
1642 o_array = [o_array arrayByAddingObject: o_dic];
1647 [self appendArray: o_array atPos: index enqueue: YES];
1649 /* This should never occur */
1650 else if( p_node->i_children == -1 )
1652 vlc_object_release( p_playlist );
1657 [self appendNodeArray: o_array inNode: p_node
1658 atPos: index enqueue: YES];
1660 vlc_object_release( p_playlist );
1663 vlc_object_release( p_playlist );
1667 /* Delegate method of NSWindow */
1668 /*- (void)windowWillClose:(NSNotification *)aNotification
1670 [o_btn_playlist setState: NSOffState];