1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videola/n dot org>
9 * Benjamin Pracht <bigben at videolab dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 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 a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
111 o_outline_dict = [[NSMutableDictionary alloc] init];
117 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
119 i_current_view = VIEW_CATEGORY;
120 playlist_ViewUpdate( p_playlist, i_current_view );
122 [o_outline_view setTarget: self];
123 [o_outline_view setDelegate: self];
124 [o_outline_view setDataSource: self];
126 vlc_object_release( p_playlist );
132 [[o_tc_name headerCell] setStringValue:_NS("Name")];
133 [[o_tc_author headerCell] setStringValue:_NS("Author")];
134 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
137 - (NSOutlineView *)outlineView
139 return o_outline_view;
142 - (playlist_item_t *)selectedPlaylistItem
144 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
150 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
152 /* return the number of children for Obj-C pointer item */ /* DONE */
153 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
156 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
158 if( p_playlist == NULL )
160 if( outlineView != o_outline_view )
162 vlc_object_release( p_playlist );
169 playlist_view_t *p_view;
170 p_view = playlist_ViewFind( p_playlist, i_current_view );
171 if( p_view && p_view->p_root )
173 i_return = p_view->p_root->i_children;
175 if( i_current_view == VIEW_CATEGORY )
177 i_return--; /* remove the GENERAL item from the list */
178 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
184 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
186 i_return = p_item->i_children;
188 vlc_object_release( p_playlist );
196 /* return the child at index for the Obj-C pointer item */ /* DONE */
197 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
199 playlist_item_t *p_return = NULL;
200 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
204 if( p_playlist == NULL )
210 playlist_view_t *p_view;
211 p_view = playlist_ViewFind( p_playlist, i_current_view );
212 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
214 if( i_current_view == VIEW_CATEGORY )
216 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
218 p_return = p_playlist->p_general->pp_children[index];
220 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
222 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
228 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
229 if( p_item && index < p_item->i_children && index >= 0 )
230 p_return = p_item->pp_children[index];
234 vlc_object_release( p_playlist );
236 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
239 o_value = [[NSValue valueWithPointer: p_return] retain];
244 /* is the item expandable */
245 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
248 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
250 if( p_playlist == NULL )
256 playlist_view_t *p_view;
257 p_view = playlist_ViewFind( p_playlist, i_current_view );
258 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
260 if( i_current_view == VIEW_CATEGORY )
263 i_return += p_playlist->p_general->i_children;
268 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
270 i_return = p_item->i_children;
272 vlc_object_release( p_playlist );
280 /* retrieve the string values for the cells */
281 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
284 intf_thread_t *p_intf = VLCIntf;
285 playlist_t *p_playlist;
286 playlist_item_t *p_item;
288 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
290 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
292 if( p_playlist == NULL )
297 p_item = (playlist_item_t *)[item pointerValue];
301 vlc_object_release( p_playlist );
305 if( [[o_tc identifier] isEqualToString:@"1"] )
307 o_value = [NSString stringWithUTF8String:
308 p_item->input.psz_name];
309 if( o_value == NULL )
310 o_value = [NSString stringWithCString:
311 p_item->input.psz_name];
313 else if( [[o_tc identifier] isEqualToString:@"2"] )
316 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
318 if( psz_temp == NULL )
322 o_value = [NSString stringWithUTF8String: psz_temp];
323 if( o_value == NULL )
325 o_value = [NSString stringWithCString: psz_temp];
330 else if( [[o_tc identifier] isEqualToString:@"3"] )
332 char psz_duration[MSTRTIME_MAX_SIZE];
333 mtime_t dur = p_item->input.i_duration;
336 secstotimestr( psz_duration, dur/1000000 );
337 o_value = [NSString stringWithUTF8String: psz_duration];
341 o_value = @"-:--:--";
344 vlc_object_release( p_playlist );
351 /*****************************************************************************
352 * VLCPlaylistWizard implementation
353 *****************************************************************************/
354 @implementation VLCPlaylistWizard
356 - (IBAction)reloadOutlineView
358 /* Only reload the outlineview if the wizard window is open since this can
359 be quite long on big playlists */
360 if( [[o_outline_view window] isVisible] )
362 [o_outline_view reloadData];
368 /*****************************************************************************
369 * VLCPlaylist implementation
370 *****************************************************************************/
371 @implementation VLCPlaylist
378 o_nodes_array = [[NSMutableArray alloc] init];
379 o_items_array = [[NSMutableArray alloc] init];
386 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
388 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
393 [super awakeFromNib];
395 [o_outline_view setDoubleAction: @selector(playItem:)];
397 [o_outline_view registerForDraggedTypes:
398 [NSArray arrayWithObjects: NSFilenamesPboardType,
399 @"VLCPlaylistItemPboardType", nil]];
400 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
402 /* We need to check whether _defaultTableHeaderSortImage exists, since it
403 belongs to an Apple hidden private API, and then can "disapear" at any time*/
405 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
407 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
411 o_ascendingSortingImage = nil;
414 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
416 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
420 o_descendingSortingImage = nil;
423 o_tc_sortColumn = nil;
425 for( i_index = 0; i_index < p_list->i_count; i_index++ )
428 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
430 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
432 /* create the menu entries used in the playlist menu */
433 o_lmi = [[o_mi_services submenu] addItemWithTitle:
434 [NSString stringWithUTF8String:
435 p_parser->psz_longname ? p_parser->psz_longname :
436 ( p_parser->psz_shortname ? p_parser->psz_shortname:
437 p_parser->psz_object_name)]
438 action: @selector(servicesChange:)
440 [o_lmi setTarget: self];
441 [o_lmi setRepresentedObject:
442 [NSString stringWithCString: p_parser->psz_object_name]];
443 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
444 p_parser->psz_object_name ) )
445 [o_lmi setState: NSOnState];
447 /* create the menu entries for the main menu */
448 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
449 [NSString stringWithUTF8String:
450 p_parser->psz_longname ? p_parser->psz_longname :
451 ( p_parser->psz_shortname ? p_parser->psz_shortname:
452 p_parser->psz_object_name)]
453 action: @selector(servicesChange:)
455 [o_lmi setTarget: self];
456 [o_lmi setRepresentedObject:
457 [NSString stringWithCString: p_parser->psz_object_name]];
458 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
459 p_parser->psz_object_name ) )
460 [o_lmi setState: NSOnState];
463 vlc_list_release( p_list );
464 vlc_object_release( p_playlist );
466 //[self playlistUpdated];
469 - (void)searchfieldChanged:(NSNotification *)o_notification
471 [o_search_field setStringValue:[[o_notification object] stringValue]];
478 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
479 [o_mi_play setTitle: _NS("Play")];
480 [o_mi_delete setTitle: _NS("Delete")];
481 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
482 [o_mi_selectall setTitle: _NS("Select All")];
483 [o_mi_info setTitle: _NS("Information")];
484 [o_mi_preparse setTitle: _NS("Get Stream Information")];
485 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
486 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
487 [o_mi_services setTitle: _NS("Services discovery")];
488 [o_status_field setStringValue: [NSString stringWithFormat:
489 _NS("No items in the playlist")]];
491 [o_random_ckb setTitle: _NS("Random")];
493 [o_search_button setTitle: _NS("Search")];
495 [o_search_field setToolTip: _NS("Search in Playlist")];
496 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
497 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
498 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
499 [o_mi_addNode setTitle: _NS("Add Folder to Playlist")];
502 - (void)playlistUpdated
506 /* Clear indications of any existing column sorting */
507 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
509 [o_outline_view setIndicatorImage:nil inTableColumn:
510 [[o_outline_view tableColumns] objectAtIndex:i]];
513 [o_outline_view setHighlightedTableColumn:nil];
514 o_tc_sortColumn = nil;
515 // TODO Find a way to keep the dict size to a minimum
516 //[o_outline_dict removeAllObjects];
517 [o_outline_view reloadData];
518 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
519 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
522 - (void)playModeUpdated
524 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
526 vlc_value_t val, val2;
528 if( p_playlist == NULL )
533 var_Get( p_playlist, "loop", &val2 );
534 var_Get( p_playlist, "repeat", &val );
535 if( val.b_bool == VLC_TRUE )
537 [o_loop_popup selectItemAtIndex: 1];
539 else if( val2.b_bool == VLC_TRUE )
541 [o_loop_popup selectItemAtIndex: 2];
545 [o_loop_popup selectItemAtIndex: 0];
548 var_Get( p_playlist, "random", &val );
549 [o_random_ckb setState: val.b_bool];
551 vlc_object_release( p_playlist );
554 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
557 for( i = 0 ; i < p_item->i_parents; i++ )
559 if( p_item->pp_parents[i]->i_view == i_current_view )
561 return p_item->pp_parents[i]->p_parent;
567 - (void)updateRowSelection
572 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
574 playlist_item_t *p_item, *p_temp_item;
575 NSMutableArray *o_array = [NSMutableArray array];
577 if( p_playlist == NULL )
580 p_item = p_playlist->status.p_item;
583 vlc_object_release(p_playlist);
587 p_temp_item = p_item;
588 while( p_temp_item->i_parents > 0 )
590 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
592 p_temp_item = [self parentOfItem: p_temp_item];
593 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
595 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
597 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
603 for (j = 0 ; j < [o_array count] - 1 ; j++)
606 if( ( o_item = [o_outline_dict objectForKey:
607 [NSString stringWithFormat: @"%p",
608 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
609 [o_outline_view expandItem: o_item];
613 i_row = [o_outline_view rowForItem:[o_outline_dict
614 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
616 [o_outline_view selectRow: i_row byExtendingSelection: NO];
617 [o_outline_view scrollRowToVisible: i_row];
619 vlc_object_release(p_playlist);
622 /* Check if p_item is a child of p_node recursively. We need to check the item
623 existence first since OSX sometimes tries to redraw items that have been
624 deleted. We don't do it when not required since this verification takes
625 quite a long time on big playlists (yes, pretty hacky). */
626 - (BOOL)isItem: (playlist_item_t *)p_item
627 inNode: (playlist_item_t *)p_node
628 checkItemExistence:(BOOL)b_check
631 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
633 playlist_item_t *p_temp_item = p_item;
635 if( p_playlist == NULL )
640 if( p_node == p_item )
642 vlc_object_release(p_playlist);
646 if( p_node->i_children < 1)
648 vlc_object_release(p_playlist);
655 vlc_mutex_lock( &p_playlist->object_lock );
659 /* Since outlineView: willDisplayCell:... may call this function with
660 p_items that don't exist anymore, first check if the item is still
661 in the playlist. Any cleaner solution welcomed. */
662 for( i = 0; i < p_playlist->i_all_size; i++ )
664 if( p_playlist->pp_all_items[i] == p_item ) break;
665 else if ( i == p_playlist->i_all_size - 1 )
667 vlc_object_release( p_playlist );
668 vlc_mutex_unlock( &p_playlist->object_lock );
674 while( p_temp_item->i_parents > 0 )
676 p_temp_item = [self parentOfItem: p_temp_item];
677 if( p_temp_item == p_node )
679 vlc_mutex_unlock( &p_playlist->object_lock );
680 vlc_object_release( p_playlist );
684 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
686 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
688 if( p_temp_item->pp_parents[i]->p_parent == p_node )
690 vlc_mutex_unlock( &p_playlist->object_lock );
691 vlc_object_release( p_playlist );
696 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
702 vlc_mutex_unlock( &p_playlist->object_lock );
705 vlc_object_release( p_playlist );
709 /* This method is usefull for instance to remove the selected children of an
710 already selected node */
711 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
714 for( i = 0 ; i < [o_items count] ; i++ )
716 for ( j = 0 ; j < [o_nodes count] ; j++ )
718 if( o_items == o_nodes)
720 if( j == i ) continue;
722 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
723 inNode: [[o_nodes objectAtIndex:j] pointerValue]
724 checkItemExistence: NO] )
726 [o_items removeObjectAtIndex:i];
727 /* We need to execute the next iteration with the same index
728 since the current item has been deleted */
737 - (IBAction)savePlaylist:(id)sender
739 intf_thread_t * p_intf = VLCIntf;
740 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
743 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
744 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
745 [o_save_panel setTitle: _NS("Save Playlist")];
746 [o_save_panel setPrompt: _NS("Save")];
748 if( [o_save_panel runModalForDirectory: nil
749 file: o_name] == NSOKButton )
751 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
753 vlc_object_release( p_playlist );
757 /* When called retrieves the selected outlineview row and plays that node or item */
758 - (IBAction)playItem:(id)sender
760 intf_thread_t * p_intf = VLCIntf;
761 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
764 if( p_playlist != NULL )
766 playlist_item_t *p_item;
767 playlist_item_t *p_node = NULL;
769 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
773 if( p_item->i_children == -1 )
775 p_node = [self parentOfItem: p_item];
777 /* for( i = 0 ; i < p_item->i_parents ; i++ )
779 if( p_item->pp_parents[i]->i_view == i_current_view )
781 p_node = p_item->pp_parents[i]->p_parent;
788 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
790 p_item = p_node->pp_children[0];
797 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
799 vlc_object_release( p_playlist );
803 /* When called retrieves the selected outlineview row and plays that node or item */
804 - (IBAction)preparseItem:(id)sender
807 NSMutableArray *o_to_preparse;
808 intf_thread_t * p_intf = VLCIntf;
809 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
812 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
813 i_count = [o_to_preparse count];
815 if( p_playlist != NULL )
819 playlist_item_t *p_item = NULL;
821 for( i = 0; i < i_count; i++ )
823 o_number = [o_to_preparse lastObject];
824 i_row = [o_number intValue];
825 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
826 [o_to_preparse removeObject: o_number];
827 [o_outline_view deselectRow: i_row];
831 if( p_item->i_children == -1 )
833 playlist_PreparseEnqueue( p_playlist, &p_item->input );
837 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
841 vlc_object_release( p_playlist );
843 [self playlistUpdated];
846 - (IBAction)servicesChange:(id)sender
848 NSMenuItem *o_mi = (NSMenuItem *)sender;
849 NSString *o_string = [o_mi representedObject];
850 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
852 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
853 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
855 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
857 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
858 [o_string cString] ) ? YES : NO];
860 i_current_view = VIEW_CATEGORY;
861 playlist_ViewUpdate( p_playlist, i_current_view );
862 vlc_object_release( p_playlist );
863 [self playlistUpdated];
867 - (IBAction)selectAll:(id)sender
869 [o_outline_view selectAll: nil];
872 - (IBAction)deleteItem:(id)sender
874 int i, i_count, i_row;
875 NSMutableArray *o_to_delete;
878 playlist_t * p_playlist;
879 intf_thread_t * p_intf = VLCIntf;
881 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
884 if ( p_playlist == NULL )
888 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
889 i_count = [o_to_delete count];
891 for( i = 0; i < i_count; i++ )
893 o_number = [o_to_delete lastObject];
894 i_row = [o_number intValue];
895 id o_item = [o_outline_view itemAtRow: i_row];
896 playlist_item_t *p_item = [o_item pointerValue];
897 [o_to_delete removeObject: o_number];
898 [o_outline_view deselectRow: i_row];
900 if( [[o_outline_view dataSource] outlineView:o_outline_view
901 numberOfChildrenOfItem: o_item] > 0 )
902 //is a node and not an item
904 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
905 [self isItem: p_playlist->status.p_item inNode:
906 ((playlist_item_t *)[o_item pointerValue])
907 checkItemExistence: NO] == YES )
909 // if current item is in selected node and is playing then stop playlist
910 playlist_Stop( p_playlist );
912 vlc_mutex_lock( &p_playlist->object_lock );
913 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
914 vlc_mutex_unlock( &p_playlist->object_lock );
918 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
919 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
921 playlist_Stop( p_playlist );
923 vlc_mutex_lock( &p_playlist->object_lock );
924 playlist_Delete( p_playlist, p_item->input.i_id );
925 vlc_mutex_unlock( &p_playlist->object_lock );
928 [self playlistUpdated];
929 vlc_object_release( p_playlist );
932 - (IBAction)sortNodeByName:(id)sender
934 [self sortNode: SORT_TITLE];
937 - (IBAction)sortNodeByAuthor:(id)sender
939 [self sortNode: SORT_AUTHOR];
942 - (void)sortNode:(int)i_mode
944 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
946 playlist_item_t * p_item;
948 if (p_playlist == NULL)
953 if( [o_outline_view selectedRow] > -1 )
955 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
959 /*If no item is selected, sort the whole playlist*/
961 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
962 p_item = p_view->p_root;
965 if( p_item->i_children > -1 ) // the item is a node
967 vlc_mutex_lock( &p_playlist->object_lock );
968 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
969 vlc_mutex_unlock( &p_playlist->object_lock );
975 for( i = 0 ; i < p_item->i_parents ; i++ )
977 if( p_item->pp_parents[i]->i_view == i_current_view )
979 vlc_mutex_lock( &p_playlist->object_lock );
980 playlist_RecursiveNodeSort( p_playlist,
981 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
982 vlc_mutex_unlock( &p_playlist->object_lock );
987 vlc_object_release( p_playlist );
988 [self playlistUpdated];
991 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
993 intf_thread_t * p_intf = VLCIntf;
994 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
997 if( p_playlist == NULL )
1001 playlist_item_t *p_item;
1003 BOOL b_rem = FALSE, b_dir = FALSE;
1004 NSString *o_uri, *o_name;
1009 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1010 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1011 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1013 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1014 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1016 int i_count, i_index;
1017 struct statfs *mounts = NULL;
1019 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1020 /* getmntinfo returns a pointer to static data. Do not free. */
1021 for( i_index = 0 ; i_index < i_count; i_index++ )
1023 NSMutableString *o_temp, *o_temp2;
1024 o_temp = [NSMutableString stringWithString: o_uri];
1025 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1026 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1027 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1028 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1030 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1032 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1036 /* If no name, then make a guess */
1037 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1039 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1040 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1041 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1043 /* All of this is to make sure CD's play when you D&D them on VLC */
1044 /* Converts mountpoint to a /dev file */
1047 NSMutableString *o_temp;
1049 buf = (struct statfs *) malloc (sizeof(struct statfs));
1050 statfs( [o_uri fileSystemRepresentation], buf );
1051 psz_dev = strdup(buf->f_mntfromname);
1052 o_temp = [NSMutableString stringWithCString: psz_dev ];
1053 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1054 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1055 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1059 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1065 for( i = 0; i < (int)[o_options count]; i++ )
1067 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1071 /* Recent documents menu */
1072 o_true_file = [NSURL fileURLWithPath: o_uri];
1073 if( o_true_file != nil )
1075 [[NSDocumentController sharedDocumentController]
1076 noteNewRecentDocumentURL: o_true_file];
1079 vlc_object_release( p_playlist );
1083 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1086 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1088 if( p_playlist == NULL )
1093 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1095 playlist_item_t *p_item;
1096 NSDictionary *o_one_item;
1099 o_one_item = [o_array objectAtIndex: i_item];
1100 p_item = [self createItem: o_one_item];
1107 playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1109 if( i_item == 0 && !b_enqueue )
1111 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1114 vlc_object_release( p_playlist );
1117 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1120 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1122 if( p_playlist == NULL )
1127 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1129 playlist_item_t *p_item;
1130 NSDictionary *o_one_item;
1133 o_one_item = [o_array objectAtIndex: i_item];
1134 p_item = [self createItem: o_one_item];
1141 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
1143 if( i_item == 0 && !b_enqueue )
1145 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1148 vlc_object_release( p_playlist );
1152 - (IBAction)handlePopUp:(id)sender
1155 intf_thread_t * p_intf = VLCIntf;
1156 vlc_value_t val1,val2;
1157 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1159 if( p_playlist == NULL )
1164 switch( [o_loop_popup indexOfSelectedItem] )
1169 var_Set( p_playlist, "loop", val1 );
1171 var_Set( p_playlist, "repeat", val1 );
1172 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1177 var_Set( p_playlist, "repeat", val1 );
1179 var_Set( p_playlist, "loop", val1 );
1180 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1184 var_Get( p_playlist, "repeat", &val1 );
1185 var_Get( p_playlist, "loop", &val2 );
1186 if( val1.b_bool || val2.b_bool )
1189 var_Set( p_playlist, "repeat", val1 );
1190 var_Set( p_playlist, "loop", val1 );
1191 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1195 vlc_object_release( p_playlist );
1196 [self playlistUpdated];
1199 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1201 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1203 playlist_item_t *p_selected_item;
1204 int i_current, i_selected_row;
1209 i_selected_row = [o_outline_view selectedRow];
1210 if (i_selected_row < 0)
1213 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1214 i_selected_row] pointerValue];
1216 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1219 NSString *o_current_name, *o_current_author;
1221 vlc_mutex_lock( &p_playlist->object_lock );
1222 o_current_name = [NSString stringWithUTF8String:
1223 p_item->pp_children[i_current]->input.psz_name];
1224 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1225 _("Meta-information"),_("Artist") );
1226 o_current_author = [NSString stringWithUTF8String: psz_temp];
1228 vlc_mutex_unlock( &p_playlist->object_lock );
1230 if( p_selected_item == p_item->pp_children[i_current] &&
1231 b_selected_item_met == NO )
1233 b_selected_item_met = YES;
1235 else if( p_selected_item == p_item->pp_children[i_current] &&
1236 b_selected_item_met == YES )
1238 vlc_object_release( p_playlist );
1241 else if( b_selected_item_met == YES &&
1242 ( [o_current_name rangeOfString:[o_search_field
1243 stringValue] options:NSCaseInsensitiveSearch ].length ||
1244 [o_current_author rangeOfString:[o_search_field
1245 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1247 vlc_object_release( p_playlist );
1248 /*Adds the parent items in the result array as well, so that we can
1250 return [NSMutableArray arrayWithObject: [NSValue
1251 valueWithPointer: p_item->pp_children[i_current]]];
1253 if( p_item->pp_children[i_current]->i_children > 0 )
1255 id o_result = [self subSearchItem:
1256 p_item->pp_children[i_current]];
1257 if( o_result != NULL )
1259 vlc_object_release( p_playlist );
1260 [o_result insertObject: [NSValue valueWithPointer:
1261 p_item->pp_children[i_current]] atIndex:0];
1266 vlc_object_release( p_playlist );
1270 - (IBAction)searchItem:(id)sender
1272 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1274 playlist_view_t * p_view;
1280 b_selected_item_met = NO;
1282 if( p_playlist == NULL )
1284 p_view = playlist_ViewFind( p_playlist, i_current_view );
1288 /*First, only search after the selected item:*
1289 *(b_selected_item_met = NO) */
1290 o_result = [self subSearchItem:p_view->p_root];
1291 if( o_result == NULL )
1293 /* If the first search failed, search again from the beginning */
1294 o_result = [self subSearchItem:p_view->p_root];
1296 if( o_result != NULL )
1299 if( [[o_result objectAtIndex: 0] pointerValue] ==
1300 p_playlist->p_general )
1305 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1307 [o_outline_view expandItem: [o_outline_dict objectForKey:
1308 [NSString stringWithFormat: @"%p",
1309 [[o_result objectAtIndex: i] pointerValue]]]];
1311 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1312 [NSString stringWithFormat: @"%p",
1313 [[o_result objectAtIndex: [o_result count] - 1 ]
1318 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1319 [o_outline_view scrollRowToVisible: i_row];
1322 vlc_object_release( p_playlist );
1325 - (IBAction)recursiveExpandNode:(id)sender
1328 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1329 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1331 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1332 isItemExpandable: o_item] )
1334 for( i = 0 ; i < p_item->i_parents ; i++ )
1336 if( p_item->pp_parents[i]->i_view == i_current_view )
1338 o_item = [o_outline_dict objectForKey: [NSString
1339 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1345 /* We need to collapse the node first, since OSX refuses to recursively
1346 expand an already expanded node, even if children nodes are collapsed. */
1347 [o_outline_view collapseItem: o_item collapseChildren: YES];
1348 [o_outline_view expandItem: o_item expandChildren: YES];
1351 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1355 vlc_bool_t b_item_sel;
1357 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1359 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1360 [o_outline_view selectedRow] != -1 );
1361 b_rows = [o_outline_view numberOfRows] != 0;
1363 [o_mi_play setEnabled: b_item_sel];
1364 [o_mi_delete setEnabled: b_item_sel];
1365 [o_mi_selectall setEnabled: b_rows];
1366 [o_mi_info setEnabled: b_item_sel];
1367 [o_mi_preparse setEnabled: b_item_sel];
1368 [o_mi_recursive_expand setEnabled: b_item_sel];
1369 [o_mi_sort_name setEnabled: b_item_sel];
1370 [o_mi_sort_author setEnabled: b_item_sel];
1372 return( o_ctx_menu );
1375 - (void)outlineView: (NSTableView*)o_tv
1376 didClickTableColumn:(NSTableColumn *)o_tc
1378 int i_mode = 0, i_type;
1379 intf_thread_t *p_intf = VLCIntf;
1380 playlist_view_t *p_view;
1382 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1384 if( p_playlist == NULL )
1389 /* Check whether the selected table column header corresponds to a
1390 sortable table column*/
1391 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1393 vlc_object_release( p_playlist );
1397 p_view = playlist_ViewFind( p_playlist, i_current_view );
1399 if( o_tc_sortColumn == o_tc )
1401 b_isSortDescending = !b_isSortDescending;
1405 b_isSortDescending = VLC_FALSE;
1408 if( o_tc == o_tc_name )
1410 i_mode = SORT_TITLE;
1412 else if( o_tc == o_tc_author )
1414 i_mode = SORT_AUTHOR;
1417 if( b_isSortDescending )
1419 i_type = ORDER_REVERSE;
1423 i_type = ORDER_NORMAL;
1426 vlc_mutex_lock( &p_playlist->object_lock );
1427 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1428 vlc_mutex_unlock( &p_playlist->object_lock );
1430 vlc_object_release( p_playlist );
1431 [self playlistUpdated];
1433 o_tc_sortColumn = o_tc;
1434 [o_outline_view setHighlightedTableColumn:o_tc];
1436 if( b_isSortDescending )
1438 [o_outline_view setIndicatorImage:o_descendingSortingImage
1439 inTableColumn:o_tc];
1443 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1444 inTableColumn:o_tc];
1449 - (void)outlineView:(NSOutlineView *)outlineView
1450 willDisplayCell:(id)cell
1451 forTableColumn:(NSTableColumn *)tableColumn
1454 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1459 if( !p_playlist ) return;
1461 o_playing_item = [o_outline_dict objectForKey:
1462 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1464 if( [self isItem: [o_playing_item pointerValue] inNode:
1465 [item pointerValue] checkItemExistence: YES]
1466 || [o_playing_item isEqual: item] )
1468 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1472 [cell setFont: [NSFont systemFontOfSize: 0]];
1474 vlc_object_release( p_playlist );
1477 - (IBAction)addNode:(id)sender
1479 /* simply adds a new node to the end of the playlist */
1480 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1487 playlist_item_t * p_item = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
1488 _("Empty Folder"), p_playlist->p_general );
1491 msg_Warn( VLCIntf, "node creation failed" );
1493 playlist_ViewUpdate( p_playlist, VIEW_CATEGORY );
1495 vlc_object_release( p_playlist );
1500 @implementation VLCPlaylist (NSOutlineViewDataSource)
1502 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1504 id o_value = [super outlineView: outlineView child: index ofItem: item];
1505 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1508 if( !p_playlist ) return nil;
1510 if( p_playlist->i_size >= 2 )
1512 [o_status_field setStringValue: [NSString stringWithFormat:
1513 _NS("%i items in the playlist"), p_playlist->i_size]];
1517 if( p_playlist->i_size == 0 )
1519 [o_status_field setStringValue: _NS("No items in the playlist")];
1523 [o_status_field setStringValue: _NS("1 item in the playlist")];
1526 vlc_object_release( p_playlist );
1528 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1529 [o_value pointerValue]]];
1535 /* Required for drag & drop and reordering */
1536 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1539 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1542 /* First remove the items that were moved during the last drag & drop
1544 [o_items_array removeAllObjects];
1545 [o_nodes_array removeAllObjects];
1547 if( !p_playlist ) return NO;
1549 for( i = 0 ; i < [items count] ; i++ )
1551 id o_item = [items objectAtIndex: i];
1553 /* Refuse to move items that are not in the General Node
1554 (Service Discovery) */
1555 if( ![self isItem: [o_item pointerValue] inNode:
1556 p_playlist->p_general checkItemExistence: NO])
1558 vlc_object_release(p_playlist);
1561 /* Fill the items and nodes to move in 2 different arrays */
1562 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1563 [o_nodes_array addObject: o_item];
1565 [o_items_array addObject: o_item];
1568 /* Now we need to check if there are selected items that are in already
1569 selected nodes. In that case, we only want to move the nodes */
1570 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1571 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1573 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1574 a Drop operation coming from the playlist. */
1576 [pboard declareTypes: [NSArray arrayWithObjects:
1577 @"VLCPlaylistItemPboardType", nil] owner: self];
1578 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1580 vlc_object_release(p_playlist);
1584 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1586 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1588 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1590 if( !p_playlist ) return NSDragOperationNone;
1592 /* Dropping ON items is not allowed if item is not a node */
1595 if( index == NSOutlineViewDropOnItemIndex &&
1596 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1598 vlc_object_release( p_playlist );
1599 return NSDragOperationNone;
1603 /* We refuse to drop an item in anything else than a child of the General
1604 Node. We still accept items that would be root nodes of the outlineview
1605 however, to allow drop in an empty playlist. */
1606 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1607 checkItemExistence: NO] || item == nil) )
1609 vlc_object_release( p_playlist );
1610 return NSDragOperationNone;
1613 /* Drop from the Playlist */
1614 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1617 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1619 /* We refuse to Drop in a child of an item we are moving */
1620 if( [self isItem: [item pointerValue] inNode:
1621 [[o_nodes_array objectAtIndex: i] pointerValue]
1622 checkItemExistence: NO] )
1624 vlc_object_release( p_playlist );
1625 return NSDragOperationNone;
1628 vlc_object_release(p_playlist);
1629 return NSDragOperationMove;
1632 /* Drop from the Finder */
1633 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1635 vlc_object_release(p_playlist);
1636 return NSDragOperationGeneric;
1638 vlc_object_release(p_playlist);
1639 return NSDragOperationNone;
1642 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1644 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1646 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1648 if( !p_playlist ) return NO;
1650 /* Drag & Drop inside the playlist */
1651 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1653 int i_row, i_removed_from_node = 0;
1655 playlist_item_t *p_new_parent, *p_item = NULL;
1656 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1658 /* If the item is to be dropped as root item of the outline, make it a
1659 child of the General node.
1660 Else, choose the proposed parent as parent. */
1661 if( item == nil ) p_new_parent = p_playlist->p_general;
1662 else p_new_parent = [item pointerValue];
1664 /* Make sure the proposed parent is a node.
1665 (This should never be true) */
1666 if( p_new_parent->i_children < 0 )
1668 vlc_object_release( p_playlist );
1672 for( i = 0; i < [o_all_items count]; i++ )
1674 playlist_item_t *p_old_parent = NULL;
1675 int i_old_index = 0;
1677 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1678 p_old_parent = [self parentOfItem: p_item];
1681 /* We may need the old index later */
1682 if( p_new_parent == p_old_parent )
1685 for( j = 0; j < p_old_parent->i_children; j++ )
1687 if( p_old_parent->pp_children[j] == p_item )
1695 vlc_mutex_lock( &p_playlist->object_lock );
1696 // Acually detach the item from the old position
1697 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1699 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1703 /* Calculate the new index */
1706 /* If we move the item in the same node, we need to take into
1707 account that one item will be deleted */
1710 if ((p_new_parent == p_old_parent &&
1711 i_old_index < index + (int)i) )
1713 i_removed_from_node++;
1715 i_new_index = index + i - i_removed_from_node;
1717 // Reattach the item to the new position
1718 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1719 p_new_parent, i_new_index );
1721 vlc_mutex_unlock( &p_playlist->object_lock );
1723 [self playlistUpdated];
1724 i_row = [o_outline_view rowForItem:[o_outline_dict
1725 objectForKey:[NSString stringWithFormat: @"%p",
1726 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1730 i_row = [o_outline_view rowForItem:[o_outline_dict
1731 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1734 [o_outline_view deselectAll: self];
1735 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1736 [o_outline_view scrollRowToVisible: i_row];
1738 vlc_object_release(p_playlist);
1742 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1745 playlist_item_t *p_node = [item pointerValue];
1747 NSArray *o_array = [NSArray array];
1748 NSArray *o_values = [[o_pasteboard propertyListForType:
1749 NSFilenamesPboardType]
1750 sortedArrayUsingSelector:
1751 @selector(caseInsensitiveCompare:)];
1753 for( i = 0; i < (int)[o_values count]; i++)
1755 NSDictionary *o_dic;
1756 o_dic = [NSDictionary dictionaryWithObject:[o_values
1757 objectAtIndex:i] forKey:@"ITEM_URL"];
1758 o_array = [o_array arrayByAddingObject: o_dic];
1763 [self appendArray: o_array atPos: index enqueue: YES];
1765 /* This should never occur */
1766 else if( p_node->i_children == -1 )
1768 vlc_object_release( p_playlist );
1773 [self appendNodeArray: o_array inNode: p_node
1774 atPos: index inView: i_current_view enqueue: YES];
1776 vlc_object_release( p_playlist );
1779 vlc_object_release( p_playlist );
1783 /* Delegate method of NSWindow */
1784 /*- (void)windowWillClose:(NSNotification *)aNotification
1786 [o_btn_playlist setState: NSOffState];