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 [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_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
154 if( p_playlist == NULL )
156 if( outlineView != o_outline_view )
158 vlc_object_release( p_playlist );
165 if( p_playlist->p_root_category )
167 i_return = p_playlist->p_root_category->i_children;
172 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
174 i_return = p_item->i_children;
176 vlc_object_release( p_playlist );
184 /* return the child at index for the Obj-C pointer item */ /* DONE */
185 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
187 playlist_item_t *p_return = NULL;
188 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
192 if( p_playlist == NULL )
198 if( p_playlist->p_root_category )
200 p_return = p_playlist->p_root_category->pp_children[index];
205 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
206 if( p_item && index < p_item->i_children && index >= 0 )
207 p_return = p_item->pp_children[index];
210 vlc_object_release( p_playlist );
212 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
215 o_value = [[NSValue valueWithPointer: p_return] retain];
220 /* is the item expandable */
221 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
224 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
226 if( p_playlist == NULL )
232 if( p_playlist->p_root_category )
234 i_return = p_playlist->p_root_category->i_children;
239 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
241 i_return = p_item->i_children;
243 vlc_object_release( p_playlist );
251 /* retrieve the string values for the cells */
252 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
255 intf_thread_t *p_intf = VLCIntf;
256 playlist_t *p_playlist;
257 playlist_item_t *p_item;
259 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
261 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
263 if( p_playlist == NULL )
268 p_item = (playlist_item_t *)[item pointerValue];
272 vlc_object_release( p_playlist );
276 if( [[o_tc identifier] isEqualToString:@"1"] )
278 o_value = [NSString stringWithUTF8String:
279 p_item->p_input->psz_name];
280 if( o_value == NULL )
281 o_value = [NSString stringWithCString:
282 p_item->p_input->psz_name];
284 else if( [[o_tc identifier] isEqualToString:@"2"] )
287 psz_temp = vlc_input_item_GetInfo( p_item->p_input ,_("Meta-information"),_("Artist") );
289 if( psz_temp == NULL )
293 o_value = [NSString stringWithUTF8String: psz_temp];
294 if( o_value == NULL )
296 o_value = [NSString stringWithCString: psz_temp];
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 = @"-:--:--";
315 vlc_object_release( p_playlist );
322 /*****************************************************************************
323 * VLCPlaylistWizard implementation
324 *****************************************************************************/
325 @implementation VLCPlaylistWizard
327 - (IBAction)reloadOutlineView
329 /* Only reload the outlineview if the wizard window is open since this can
330 be quite long on big playlists */
331 if( [[o_outline_view window] isVisible] )
333 [o_outline_view reloadData];
339 /*****************************************************************************
340 * VLCPlaylist implementation
341 *****************************************************************************/
342 @implementation VLCPlaylist
349 o_nodes_array = [[NSMutableArray alloc] init];
350 o_items_array = [[NSMutableArray alloc] init];
357 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
359 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
364 [super awakeFromNib];
366 [o_outline_view setDoubleAction: @selector(playItem:)];
368 [o_outline_view registerForDraggedTypes:
369 [NSArray arrayWithObjects: NSFilenamesPboardType,
370 @"VLCPlaylistItemPboardType", nil]];
371 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
373 /* We need to check whether _defaultTableHeaderSortImage exists, since it
374 belongs to an Apple hidden private API, and then can "disapear" at any time*/
376 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
378 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
382 o_ascendingSortingImage = nil;
385 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
387 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
391 o_descendingSortingImage = nil;
394 o_tc_sortColumn = nil;
396 for( i_index = 0; i_index < p_list->i_count; i_index++ )
399 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
401 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
403 /* create the menu entries used in the playlist menu */
404 o_lmi = [[o_mi_services submenu] addItemWithTitle:
405 [NSString stringWithUTF8String:
406 p_parser->psz_longname ? p_parser->psz_longname :
407 ( p_parser->psz_shortname ? p_parser->psz_shortname:
408 p_parser->psz_object_name)]
409 action: @selector(servicesChange:)
411 [o_lmi setTarget: self];
412 [o_lmi setRepresentedObject:
413 [NSString stringWithCString: p_parser->psz_object_name]];
414 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
415 p_parser->psz_object_name ) )
416 [o_lmi setState: NSOnState];
418 /* create the menu entries for the main menu */
419 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
420 [NSString stringWithUTF8String:
421 p_parser->psz_longname ? p_parser->psz_longname :
422 ( p_parser->psz_shortname ? p_parser->psz_shortname:
423 p_parser->psz_object_name)]
424 action: @selector(servicesChange:)
426 [o_lmi setTarget: self];
427 [o_lmi setRepresentedObject:
428 [NSString stringWithCString: p_parser->psz_object_name]];
429 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
430 p_parser->psz_object_name ) )
431 [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")]];
462 [o_random_ckb setTitle: _NS("Random")];
464 [o_search_button setTitle: _NS("Search")];
466 [o_search_field setToolTip: _NS("Search in Playlist")];
467 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
468 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
469 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
470 [o_mi_addNode setTitle: _NS("Add Folder to Playlist")];
472 [o_save_accessory_text setStringValue: _NS("File Format:")];
473 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
474 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
477 - (void)playlistUpdated
481 /* Clear indications of any existing column sorting */
482 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
484 [o_outline_view setIndicatorImage:nil inTableColumn:
485 [[o_outline_view tableColumns] objectAtIndex:i]];
488 [o_outline_view setHighlightedTableColumn:nil];
489 o_tc_sortColumn = nil;
490 // TODO Find a way to keep the dict size to a minimum
491 //[o_outline_dict removeAllObjects];
492 [o_outline_view reloadData];
493 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
494 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
496 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
501 if( p_playlist->i_size >= 2 )
503 [o_status_field setStringValue: [NSString stringWithFormat:
504 _NS("%i items in the playlist"), p_playlist->i_size]];
508 if( p_playlist->i_size == 0 )
510 [o_status_field setStringValue: _NS("No items in the playlist")];
514 [o_status_field setStringValue: _NS("1 item in the playlist")];
517 vlc_object_release( p_playlist );
520 - (void)playModeUpdated
522 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
524 vlc_value_t val, val2;
526 if( p_playlist == NULL )
531 var_Get( p_playlist, "loop", &val2 );
532 var_Get( p_playlist, "repeat", &val );
533 if( val.b_bool == VLC_TRUE )
535 [o_loop_popup selectItemAtIndex: 1];
537 else if( val2.b_bool == VLC_TRUE )
539 [o_loop_popup selectItemAtIndex: 2];
543 [o_loop_popup selectItemAtIndex: 0];
546 var_Get( p_playlist, "random", &val );
547 [o_random_ckb setState: val.b_bool];
549 vlc_object_release( p_playlist );
552 - (void)updateRowSelection
557 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
559 playlist_item_t *p_item, *p_temp_item;
560 NSMutableArray *o_array = [NSMutableArray array];
562 if( p_playlist == NULL )
565 p_item = p_playlist->status.p_item;
568 vlc_object_release(p_playlist);
572 p_temp_item = p_item;
573 while( p_temp_item->p_parent )
575 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
576 p_temp_item = p_temp_item->p_parent;
577 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
579 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
581 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
587 for( j = 0; j < [o_array count] - 1; j++ )
590 if( ( o_item = [o_outline_dict objectForKey:
591 [NSString stringWithFormat: @"%p",
592 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
594 msg_Err( p_playlist, "o_item: %p", o_item );
595 [o_outline_view expandItem: o_item];
600 i_row = [o_outline_view rowForItem:[o_outline_dict
601 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
603 [o_outline_view selectRow: i_row byExtendingSelection: NO];
604 [o_outline_view scrollRowToVisible: i_row];
606 vlc_object_release(p_playlist);
608 /* update our info-panel to reflect the new item */
609 [[[VLCMain sharedInstance] getInfo] updatePanel];
612 /* Check if p_item is a child of p_node recursively. We need to check the item
613 existence first since OSX sometimes tries to redraw items that have been
614 deleted. We don't do it when not required since this verification takes
615 quite a long time on big playlists (yes, pretty hacky). */
616 - (BOOL)isItem: (playlist_item_t *)p_item
617 inNode: (playlist_item_t *)p_node
618 checkItemExistence:(BOOL)b_check
621 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
623 playlist_item_t *p_temp_item = p_item;
625 if( p_playlist == NULL )
630 if( p_node == p_item )
632 vlc_object_release(p_playlist);
636 if( p_node->i_children < 1)
638 vlc_object_release(p_playlist);
645 vlc_mutex_lock( &p_playlist->object_lock );
649 /* Since outlineView: willDisplayCell:... may call this function with
650 p_items that don't exist anymore, first check if the item is still
651 in the playlist. Any cleaner solution welcomed. */
652 for( i = 0; i < p_playlist->i_all_size; i++ )
654 if( p_playlist->pp_all_items[i] == p_item ) break;
655 else if ( i == p_playlist->i_all_size - 1 )
657 vlc_object_release( p_playlist );
658 vlc_mutex_unlock( &p_playlist->object_lock );
666 p_temp_item = p_temp_item->p_parent;
667 if( p_temp_item == p_node )
669 vlc_mutex_unlock( &p_playlist->object_lock );
670 vlc_object_release( p_playlist );
674 vlc_mutex_unlock( &p_playlist->object_lock );
677 vlc_object_release( p_playlist );
681 /* This method is usefull for instance to remove the selected children of an
682 already selected node */
683 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
686 for( i = 0 ; i < [o_items count] ; i++ )
688 for ( j = 0 ; j < [o_nodes count] ; j++ )
690 if( o_items == o_nodes)
692 if( j == i ) continue;
694 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
695 inNode: [[o_nodes objectAtIndex:j] pointerValue]
696 checkItemExistence: NO] )
698 [o_items removeObjectAtIndex:i];
699 /* We need to execute the next iteration with the same index
700 since the current item has been deleted */
709 - (IBAction)savePlaylist:(id)sender
711 intf_thread_t * p_intf = VLCIntf;
712 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
715 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
716 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
718 //[o_save_panel setAllowedFileTypes: [NSArray arrayWithObjects: @"m3u", @"xpf", nil] ];
719 [o_save_panel setTitle: _NS("Save Playlist")];
720 [o_save_panel setPrompt: _NS("Save")];
721 [o_save_panel setAccessoryView: o_save_accessory_view];
723 if( [o_save_panel runModalForDirectory: nil
724 file: o_name] == NSOKButton )
726 NSString *o_filename = [o_save_panel filename];
728 if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
730 NSString * o_real_filename;
732 range.location = [o_filename length] - [@".xspf" length];
733 range.length = [@".xspf" length];
735 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
736 range: range] != NSOrderedSame )
738 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
742 o_real_filename = o_filename;
744 playlist_Export( p_playlist,
745 [o_real_filename fileSystemRepresentation],
746 p_playlist->p_local_category, "export-xspf" );
750 NSString * o_real_filename;
752 range.location = [o_filename length] - [@".m3u" length];
753 range.length = [@".m3u" length];
755 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
756 range: range] != NSOrderedSame )
758 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
762 o_real_filename = o_filename;
764 playlist_Export( p_playlist,
765 [o_real_filename fileSystemRepresentation],
766 p_playlist->p_local_category, "export-m3u" );
769 vlc_object_release( p_playlist );
772 /* When called retrieves the selected outlineview row and plays that node or item */
773 - (IBAction)playItem:(id)sender
775 intf_thread_t * p_intf = VLCIntf;
776 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
779 if( p_playlist != NULL )
781 playlist_item_t *p_item;
782 playlist_item_t *p_node = NULL;
784 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
788 if( p_item->i_children == -1 )
790 p_node = p_item->p_parent;
796 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
798 p_item = p_node->pp_children[0];
805 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, 0, p_node, p_item );
807 vlc_object_release( p_playlist );
811 /* When called retrieves the selected outlineview row and plays that node or item */
812 - (IBAction)preparseItem:(id)sender
815 NSMutableArray *o_to_preparse;
816 intf_thread_t * p_intf = VLCIntf;
817 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
820 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
821 i_count = [o_to_preparse count];
823 if( p_playlist != NULL )
827 playlist_item_t *p_item = NULL;
829 for( i = 0; i < i_count; i++ )
831 o_number = [o_to_preparse lastObject];
832 i_row = [o_number intValue];
833 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
834 [o_to_preparse removeObject: o_number];
835 [o_outline_view deselectRow: i_row];
839 if( p_item->i_children == -1 )
841 playlist_PreparseEnqueue( p_playlist, p_item->p_input );
845 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
849 vlc_object_release( p_playlist );
851 [self playlistUpdated];
854 - (IBAction)servicesChange:(id)sender
856 NSMenuItem *o_mi = (NSMenuItem *)sender;
857 NSString *o_string = [o_mi representedObject];
858 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
860 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
861 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
863 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
865 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
866 [o_string cString] ) ? YES : NO];
868 vlc_object_release( p_playlist );
869 [self playlistUpdated];
873 - (IBAction)selectAll:(id)sender
875 [o_outline_view selectAll: nil];
878 - (IBAction)deleteItem:(id)sender
880 int i, i_count, i_row;
881 NSMutableArray *o_to_delete;
884 playlist_t * p_playlist;
885 intf_thread_t * p_intf = VLCIntf;
887 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
890 if ( p_playlist == NULL )
894 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
895 i_count = [o_to_delete count];
897 for( i = 0; i < i_count; i++ )
899 o_number = [o_to_delete lastObject];
900 i_row = [o_number intValue];
901 id o_item = [o_outline_view itemAtRow: i_row];
902 playlist_item_t *p_item = [o_item pointerValue];
903 [o_to_delete removeObject: o_number];
904 [o_outline_view deselectRow: i_row];
906 if( [[o_outline_view dataSource] outlineView:o_outline_view
907 numberOfChildrenOfItem: o_item] > 0 )
908 //is a node and not an item
910 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
911 [self isItem: p_playlist->status.p_item inNode:
912 ((playlist_item_t *)[o_item pointerValue])
913 checkItemExistence: NO] == YES )
915 // if current item is in selected node and is playing then stop playlist
916 playlist_Stop( p_playlist );
918 vlc_mutex_lock( &p_playlist->object_lock );
919 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
920 vlc_mutex_unlock( &p_playlist->object_lock );
924 playlist_LockDelete( p_playlist, p_item->i_id );
927 [self playlistUpdated];
928 vlc_object_release( p_playlist );
931 - (IBAction)sortNodeByName:(id)sender
933 [self sortNode: SORT_TITLE];
936 - (IBAction)sortNodeByAuthor:(id)sender
938 [self sortNode: SORT_AUTHOR];
941 - (void)sortNode:(int)i_mode
943 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
945 playlist_item_t * p_item;
947 if (p_playlist == NULL)
952 if( [o_outline_view selectedRow] > -1 )
954 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
958 /*If no item is selected, sort the whole playlist*/
960 p_item = p_playlist->p_root_category;
963 if( p_item->i_children > -1 ) // the item is a node
965 vlc_mutex_lock( &p_playlist->object_lock );
966 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
967 vlc_mutex_unlock( &p_playlist->object_lock );
971 vlc_mutex_lock( &p_playlist->object_lock );
972 playlist_RecursiveNodeSort( p_playlist,
973 p_item->p_parent, i_mode, ORDER_NORMAL );
974 vlc_mutex_unlock( &p_playlist->object_lock );
976 vlc_object_release( p_playlist );
977 [self playlistUpdated];
980 - (input_item_t *)createItem:(NSDictionary *)o_one_item
982 intf_thread_t * p_intf = VLCIntf;
983 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
986 if( p_playlist == NULL )
990 input_item_t *p_input;
992 BOOL b_rem = FALSE, b_dir = FALSE;
993 NSString *o_uri, *o_name;
998 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
999 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1000 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1002 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1003 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1005 int i_count, i_index;
1006 struct statfs *mounts = NULL;
1008 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1009 /* getmntinfo returns a pointer to static data. Do not free. */
1010 for( i_index = 0 ; i_index < i_count; i_index++ )
1012 NSMutableString *o_temp, *o_temp2;
1013 o_temp = [NSMutableString stringWithString: o_uri];
1014 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1015 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1016 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1017 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1019 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1021 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1025 /* If no name, then make a guess */
1026 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1028 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1029 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1030 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1032 /* All of this is to make sure CD's play when you D&D them on VLC */
1033 /* Converts mountpoint to a /dev file */
1036 NSMutableString *o_temp;
1038 buf = (struct statfs *) malloc (sizeof(struct statfs));
1039 statfs( [o_uri fileSystemRepresentation], buf );
1040 psz_dev = strdup(buf->f_mntfromname);
1041 o_temp = [NSMutableString stringWithCString: psz_dev ];
1042 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1043 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1044 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1048 p_input = input_ItemNew( p_playlist, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1054 for( i = 0; i < (int)[o_options count]; i++ )
1056 vlc_input_item_AddOption( p_input, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1060 /* Recent documents menu */
1061 o_true_file = [NSURL fileURLWithPath: o_uri];
1062 if( o_true_file != nil )
1064 [[NSDocumentController sharedDocumentController]
1065 noteNewRecentDocumentURL: o_true_file];
1068 vlc_object_release( p_playlist );
1072 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1075 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1077 if( p_playlist == NULL )
1082 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1084 input_item_t *p_input;
1085 NSDictionary *o_one_item;
1088 o_one_item = [o_array objectAtIndex: i_item];
1089 p_input = [self createItem: o_one_item];
1096 playlist_PlaylistAddInput( p_playlist, p_input, PLAYLIST_INSERT,
1097 i_position == -1 ? PLAYLIST_END : i_position + i_item );
1099 if( i_item == 0 && !b_enqueue )
1101 playlist_item_t *p_item;
1102 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1103 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1107 playlist_item_t *p_item;
1108 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1109 playlist_Control( p_playlist, PLAYLIST_PREPARSE, p_item );
1112 [self playlistUpdated];
1113 vlc_object_release( p_playlist );
1116 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position enqueue:(BOOL)b_enqueue
1119 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1121 if( p_playlist == NULL )
1126 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1128 input_item_t *p_input;
1129 NSDictionary *o_one_item;
1132 o_one_item = [o_array objectAtIndex: i_item];
1133 p_input = [self createItem: o_one_item];
1140 playlist_NodeAddInput( p_playlist, p_input, p_node,
1143 PLAYLIST_END : i_position + i_item );
1146 if( i_item == 0 && !b_enqueue )
1148 playlist_item_t *p_item;
1149 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1150 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1154 playlist_item_t *p_item;
1155 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1156 playlist_Control( p_playlist, PLAYLIST_PREPARSE, p_item );
1159 [self playlistUpdated];
1160 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]->p_input->psz_name];
1235 psz_temp = vlc_input_item_GetInfo( p_item->p_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,
1290 b_selected_item_met = NO;
1292 if( p_playlist == NULL )
1295 /*First, only search after the selected item:*
1296 *(b_selected_item_met = NO) */
1297 o_result = [self subSearchItem:p_playlist->p_root_category];
1298 if( o_result == NULL )
1300 /* If the first search failed, search again from the beginning */
1301 o_result = [self subSearchItem:p_playlist->p_root_category];
1303 if( o_result != NULL )
1306 if( [[o_result objectAtIndex: 0] pointerValue] ==
1307 p_playlist->p_local_category )
1312 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1314 [o_outline_view expandItem: [o_outline_dict objectForKey:
1315 [NSString stringWithFormat: @"%p",
1316 [[o_result objectAtIndex: i] pointerValue]]]];
1318 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1319 [NSString stringWithFormat: @"%p",
1320 [[o_result objectAtIndex: [o_result count] - 1 ]
1325 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1326 [o_outline_view scrollRowToVisible: i_row];
1328 vlc_object_release( p_playlist );
1331 - (IBAction)recursiveExpandNode:(id)sender
1333 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1334 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1336 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1337 isItemExpandable: o_item] )
1339 o_item = [o_outline_dict objectForKey: [NSString
1340 stringWithFormat: @"%p", p_item->p_parent]];
1343 /* We need to collapse the node first, since OSX refuses to recursively
1344 expand an already expanded node, even if children nodes are collapsed. */
1345 [o_outline_view collapseItem: o_item collapseChildren: YES];
1346 [o_outline_view expandItem: o_item expandChildren: YES];
1349 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1353 vlc_bool_t b_item_sel;
1355 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1357 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1358 [o_outline_view selectedRow] != -1 );
1359 b_rows = [o_outline_view numberOfRows] != 0;
1361 [o_mi_play setEnabled: b_item_sel];
1362 [o_mi_delete setEnabled: b_item_sel];
1363 [o_mi_selectall setEnabled: b_rows];
1364 [o_mi_info setEnabled: b_item_sel];
1365 [o_mi_preparse setEnabled: b_item_sel];
1366 [o_mi_recursive_expand setEnabled: b_item_sel];
1367 [o_mi_sort_name setEnabled: b_item_sel];
1368 [o_mi_sort_author setEnabled: b_item_sel];
1370 return( o_ctx_menu );
1373 - (void)outlineView: (NSTableView*)o_tv
1374 didClickTableColumn:(NSTableColumn *)o_tc
1376 int i_mode = 0, i_type;
1377 intf_thread_t *p_intf = VLCIntf;
1379 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1381 if( p_playlist == NULL )
1386 /* Check whether the selected table column header corresponds to a
1387 sortable table column*/
1388 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1390 vlc_object_release( p_playlist );
1394 if( o_tc_sortColumn == o_tc )
1396 b_isSortDescending = !b_isSortDescending;
1400 b_isSortDescending = VLC_FALSE;
1403 if( o_tc == o_tc_name )
1405 i_mode = SORT_TITLE;
1407 else if( o_tc == o_tc_author )
1409 i_mode = SORT_AUTHOR;
1412 if( b_isSortDescending )
1414 i_type = ORDER_REVERSE;
1418 i_type = ORDER_NORMAL;
1421 vlc_mutex_lock( &p_playlist->object_lock );
1422 playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_category, i_mode, i_type );
1423 vlc_mutex_unlock( &p_playlist->object_lock );
1425 vlc_object_release( p_playlist );
1426 [self playlistUpdated];
1428 o_tc_sortColumn = o_tc;
1429 [o_outline_view setHighlightedTableColumn:o_tc];
1431 if( b_isSortDescending )
1433 [o_outline_view setIndicatorImage:o_descendingSortingImage
1434 inTableColumn:o_tc];
1438 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1439 inTableColumn:o_tc];
1444 - (void)outlineView:(NSOutlineView *)outlineView
1445 willDisplayCell:(id)cell
1446 forTableColumn:(NSTableColumn *)tableColumn
1449 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1454 if( !p_playlist ) return;
1456 o_playing_item = [o_outline_dict objectForKey:
1457 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1459 if( [self isItem: [o_playing_item pointerValue] inNode:
1460 [item pointerValue] checkItemExistence: YES]
1461 || [o_playing_item isEqual: item] )
1463 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1467 [cell setFont: [NSFont systemFontOfSize: 0]];
1469 vlc_object_release( p_playlist );
1472 - (IBAction)addNode:(id)sender
1474 /* simply adds a new node to the end of the playlist */
1475 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1482 playlist_item_t * p_item = playlist_NodeCreate( p_playlist,
1483 _("Empty Folder"), p_playlist->p_local_category );
1486 msg_Warn( VLCIntf, "node creation failed" );
1488 vlc_object_release( p_playlist );
1489 [self playlistUpdated];
1494 @implementation VLCPlaylist (NSOutlineViewDataSource)
1496 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1498 id o_value = [super outlineView: outlineView child: index ofItem: item];
1499 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1502 if( !p_playlist ) return nil;
1504 if( p_playlist->i_size >= 2 )
1506 [o_status_field setStringValue: [NSString stringWithFormat:
1507 _NS("%i items in the playlist"), p_playlist->i_size]];
1511 if( p_playlist->i_size == 0 )
1513 [o_status_field setStringValue: _NS("No items in the playlist")];
1517 [o_status_field setStringValue: _NS("1 item in the playlist")];
1520 vlc_object_release( p_playlist );
1522 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1523 [o_value pointerValue]]];
1529 /* Required for drag & drop and reordering */
1530 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1533 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1536 /* First remove the items that were moved during the last drag & drop
1538 [o_items_array removeAllObjects];
1539 [o_nodes_array removeAllObjects];
1541 if( !p_playlist ) return NO;
1543 for( i = 0 ; i < [items count] ; i++ )
1545 id o_item = [items objectAtIndex: i];
1547 /* Refuse to move items that are not in the General Node
1548 (Service Discovery) */
1549 if( ![self isItem: [o_item pointerValue] inNode:
1550 p_playlist->p_local_category checkItemExistence: NO])
1552 vlc_object_release(p_playlist);
1555 /* Fill the items and nodes to move in 2 different arrays */
1556 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1557 [o_nodes_array addObject: o_item];
1559 [o_items_array addObject: o_item];
1562 /* Now we need to check if there are selected items that are in already
1563 selected nodes. In that case, we only want to move the nodes */
1564 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1565 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1567 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1568 a Drop operation coming from the playlist. */
1570 [pboard declareTypes: [NSArray arrayWithObjects:
1571 @"VLCPlaylistItemPboardType", nil] owner: self];
1572 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1574 vlc_object_release(p_playlist);
1578 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1580 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1582 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1584 if( !p_playlist ) return NSDragOperationNone;
1586 /* Dropping ON items is not allowed if item is not a node */
1589 if( index == NSOutlineViewDropOnItemIndex &&
1590 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1592 vlc_object_release( p_playlist );
1593 return NSDragOperationNone;
1597 /* We refuse to drop an item in anything else than a child of the General
1598 Node. We still accept items that would be root nodes of the outlineview
1599 however, to allow drop in an empty playlist. */
1600 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_local_category
1601 checkItemExistence: NO] || item == nil) )
1603 vlc_object_release( p_playlist );
1604 return NSDragOperationNone;
1607 /* Drop from the Playlist */
1608 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1611 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1613 /* We refuse to Drop in a child of an item we are moving */
1614 if( [self isItem: [item pointerValue] inNode:
1615 [[o_nodes_array objectAtIndex: i] pointerValue]
1616 checkItemExistence: NO] )
1618 vlc_object_release( p_playlist );
1619 return NSDragOperationNone;
1622 vlc_object_release(p_playlist);
1623 return NSDragOperationMove;
1626 /* Drop from the Finder */
1627 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1629 vlc_object_release(p_playlist);
1630 return NSDragOperationGeneric;
1632 vlc_object_release(p_playlist);
1633 return NSDragOperationNone;
1636 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1638 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1640 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1642 if( !p_playlist ) return NO;
1644 /* Drag & Drop inside the playlist */
1645 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1647 int i_row, i_removed_from_node = 0;
1649 playlist_item_t *p_new_parent, *p_item = NULL;
1650 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1652 /* If the item is to be dropped as root item of the outline, make it a
1653 child of the General node.
1654 Else, choose the proposed parent as parent. */
1655 if( item == nil ) p_new_parent = p_playlist->p_local_category;
1656 else p_new_parent = [item pointerValue];
1658 /* Make sure the proposed parent is a node.
1659 (This should never be true) */
1660 if( p_new_parent->i_children < 0 )
1662 vlc_object_release( p_playlist );
1666 for( i = 0; i < [o_all_items count]; i++ )
1668 playlist_item_t *p_old_parent = NULL;
1669 int i_old_index = 0;
1671 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1672 p_old_parent = p_item->p_parent;
1675 /* We may need the old index later */
1676 if( p_new_parent == p_old_parent )
1679 for( j = 0; j < p_old_parent->i_children; j++ )
1681 if( p_old_parent->pp_children[j] == p_item )
1689 vlc_mutex_lock( &p_playlist->object_lock );
1690 // Acually detach the item from the old position
1691 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1695 /* Calculate the new index */
1698 /* If we move the item in the same node, we need to take into
1699 account that one item will be deleted */
1702 if ((p_new_parent == p_old_parent &&
1703 i_old_index < index + (int)i) )
1705 i_removed_from_node++;
1707 i_new_index = index + i - i_removed_from_node;
1709 // Reattach the item to the new position
1710 playlist_NodeInsert( p_playlist, p_item, p_new_parent, i_new_index );
1712 vlc_mutex_unlock( &p_playlist->object_lock );
1714 [self playlistUpdated];
1715 i_row = [o_outline_view rowForItem:[o_outline_dict
1716 objectForKey:[NSString stringWithFormat: @"%p",
1717 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1721 i_row = [o_outline_view rowForItem:[o_outline_dict
1722 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1725 [o_outline_view deselectAll: self];
1726 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1727 [o_outline_view scrollRowToVisible: i_row];
1729 vlc_object_release(p_playlist);
1733 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1736 playlist_item_t *p_node = [item pointerValue];
1738 NSArray *o_array = [NSArray array];
1739 NSArray *o_values = [[o_pasteboard propertyListForType:
1740 NSFilenamesPboardType]
1741 sortedArrayUsingSelector:
1742 @selector(caseInsensitiveCompare:)];
1744 for( i = 0; i < (int)[o_values count]; i++)
1746 NSDictionary *o_dic;
1747 o_dic = [NSDictionary dictionaryWithObject:[o_values
1748 objectAtIndex:i] forKey:@"ITEM_URL"];
1749 o_array = [o_array arrayByAddingObject: o_dic];
1754 [self appendArray: o_array atPos: index enqueue: YES];
1756 /* This should never occur */
1757 else if( p_node->i_children == -1 )
1759 vlc_object_release( p_playlist );
1764 [self appendNodeArray: o_array inNode: p_node
1765 atPos: index enqueue: YES];
1767 vlc_object_release( p_playlist );
1770 vlc_object_release( p_playlist );
1774 /* Delegate method of NSWindow */
1775 /*- (void)windowWillClose:(NSNotification *)aNotification
1777 [o_btn_playlist setState: NSOffState];