1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videola/n dot org>
9 * Benjamin Pracht <bigben at videolab dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
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 /* Change the simple textfield into a searchField if we can... */
468 if( MACOS_VERSION >= 10.3 )
470 NSView *o_parentview = [o_status_field superview];
471 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
472 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
473 [o_better_search_field setDelegate:self];
474 [[NSNotificationCenter defaultCenter] addObserver: self
475 selector: @selector(searchfieldChanged:)
476 name: NSControlTextDidChangeNotification
477 object: o_better_search_field];
479 [o_better_search_field setTarget:self];
480 [o_better_search_field setAction:@selector(searchItem:)];
482 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
483 [o_parentview addSubview:o_better_search_field];
484 [o_search_field setHidden:YES];
487 //[self playlistUpdated];
490 - (void)searchfieldChanged:(NSNotification *)o_notification
492 [o_search_field setStringValue:[[o_notification object] stringValue]];
499 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
500 [o_mi_play setTitle: _NS("Play")];
501 [o_mi_delete setTitle: _NS("Delete")];
502 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
503 [o_mi_selectall setTitle: _NS("Select All")];
504 [o_mi_info setTitle: _NS("Properties")];
505 [o_mi_preparse setTitle: _NS("Preparse")];
506 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
507 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
508 [o_mi_services setTitle: _NS("Services discovery")];
509 [o_status_field setStringValue: [NSString stringWithFormat:
510 _NS("no items in playlist")]];
512 [o_random_ckb setTitle: _NS("Random")];
514 [o_search_button setTitle: _NS("Search")];
516 [o_search_field setToolTip: _NS("Search in Playlist")];
517 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
518 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
519 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
522 - (void)playlistUpdated
526 /* Clear indications of any existing column sorting*/
527 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
529 [o_outline_view setIndicatorImage:nil inTableColumn:
530 [[o_outline_view tableColumns] objectAtIndex:i]];
533 [o_outline_view setHighlightedTableColumn:nil];
534 o_tc_sortColumn = nil;
535 // TODO Find a way to keep the dict size to a minimum
536 //[o_outline_dict removeAllObjects];
537 [o_outline_view reloadData];
538 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
539 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
542 - (void)playModeUpdated
544 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
546 vlc_value_t val, val2;
548 if( p_playlist == NULL )
553 var_Get( p_playlist, "loop", &val2 );
554 var_Get( p_playlist, "repeat", &val );
555 if( val.b_bool == VLC_TRUE )
557 [o_loop_popup selectItemAtIndex: 1];
559 else if( val2.b_bool == VLC_TRUE )
561 [o_loop_popup selectItemAtIndex: 2];
565 [o_loop_popup selectItemAtIndex: 0];
568 var_Get( p_playlist, "random", &val );
569 [o_random_ckb setState: val.b_bool];
571 vlc_object_release( p_playlist );
574 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
577 for( i = 0 ; i < p_item->i_parents; i++ )
579 if( p_item->pp_parents[i]->i_view == i_current_view )
581 return p_item->pp_parents[i]->p_parent;
587 - (void)updateRowSelection
592 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
594 playlist_item_t *p_item, *p_temp_item;
595 NSMutableArray *o_array = [NSMutableArray array];
597 if( p_playlist == NULL )
600 p_item = p_playlist->status.p_item;
603 vlc_object_release(p_playlist);
607 p_temp_item = p_item;
608 while( p_temp_item->i_parents > 0 )
610 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
612 p_temp_item = [self parentOfItem: p_temp_item];
613 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
615 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
617 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
623 for (j = 0 ; j < [o_array count] - 1 ; j++)
626 if( ( o_item = [o_outline_dict objectForKey:
627 [NSString stringWithFormat: @"%p",
628 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
629 [o_outline_view expandItem: o_item];
633 i_row = [o_outline_view rowForItem:[o_outline_dict
634 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
636 [o_outline_view selectRow: i_row byExtendingSelection: NO];
637 [o_outline_view scrollRowToVisible: i_row];
639 vlc_object_release(p_playlist);
642 /* Check if p_item is a child of p_node recursively. We need to check the item
643 existence first since OSX sometimes tries to redraw items that have been
644 deleted. We don't do it when not required since this verification takes
645 quite a long time on big playlists (yes, pretty hacky). */
646 - (BOOL)isItem: (playlist_item_t *)p_item
647 inNode: (playlist_item_t *)p_node
648 checkItemExistence:(BOOL)b_check
651 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
653 playlist_item_t *p_temp_item = p_item;
655 if( p_playlist == NULL )
660 if( p_node == p_item )
662 vlc_object_release(p_playlist);
666 if( p_node->i_children < 1)
668 vlc_object_release(p_playlist);
675 vlc_mutex_lock( &p_playlist->object_lock );
679 /* Since outlineView: willDisplayCell:... may call this function with
680 p_items that don't exist anymore, first check if the item is still
681 in the playlist. Any cleaner solution welcomed. */
682 for( i = 0; i < p_playlist->i_all_size; i++ )
684 if( p_playlist->pp_all_items[i] == p_item ) break;
685 else if ( i == p_playlist->i_all_size - 1 )
687 vlc_object_release( p_playlist );
688 vlc_mutex_unlock( &p_playlist->object_lock );
694 while( p_temp_item->i_parents > 0 )
696 p_temp_item = [self parentOfItem: p_temp_item];
697 if( p_temp_item == p_node )
699 vlc_mutex_unlock( &p_playlist->object_lock );
700 vlc_object_release( p_playlist );
704 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
706 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
708 if( p_temp_item->pp_parents[i]->p_parent == p_node )
710 vlc_mutex_unlock( &p_playlist->object_lock );
711 vlc_object_release( p_playlist );
716 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
722 vlc_mutex_unlock( &p_playlist->object_lock );
725 vlc_object_release( p_playlist );
729 /* This method is usefull for instance to remove the selected children of an
730 already selected node */
731 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
734 for( i = 0 ; i < [o_items count] ; i++ )
736 for ( j = 0 ; j < [o_nodes count] ; j++ )
738 if( o_items == o_nodes)
740 if( j == i ) continue;
742 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
743 inNode: [[o_nodes objectAtIndex:j] pointerValue]
744 checkItemExistence: NO] )
746 [o_items removeObjectAtIndex:i];
747 /* We need to execute the next iteration with the same index
748 since the current item has been deleted */
757 - (IBAction)savePlaylist:(id)sender
759 intf_thread_t * p_intf = VLCIntf;
760 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
763 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
764 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
765 [o_save_panel setTitle: _NS("Save Playlist")];
766 [o_save_panel setPrompt: _NS("Save")];
768 if( [o_save_panel runModalForDirectory: nil
769 file: o_name] == NSOKButton )
771 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
773 vlc_object_release( p_playlist );
777 /* When called retrieves the selected outlineview row and plays that node or item */
778 - (IBAction)playItem:(id)sender
780 intf_thread_t * p_intf = VLCIntf;
781 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
784 if( p_playlist != NULL )
786 playlist_item_t *p_item;
787 playlist_item_t *p_node = NULL;
789 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
793 if( p_item->i_children == -1 )
795 p_node = [self parentOfItem: p_item];
797 /* for( i = 0 ; i < p_item->i_parents ; i++ )
799 if( p_item->pp_parents[i]->i_view == i_current_view )
801 p_node = p_item->pp_parents[i]->p_parent;
808 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
810 p_item = p_node->pp_children[0];
817 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
819 vlc_object_release( p_playlist );
823 /* When called retrieves the selected outlineview row and plays that node or item */
824 - (IBAction)preparseItem:(id)sender
827 NSMutableArray *o_to_preparse;
828 intf_thread_t * p_intf = VLCIntf;
829 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
832 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
833 i_count = [o_to_preparse count];
835 if( p_playlist != NULL )
839 playlist_item_t *p_item = NULL;
841 for( i = 0; i < i_count; i++ )
843 o_number = [o_to_preparse lastObject];
844 i_row = [o_number intValue];
845 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
846 [o_to_preparse removeObject: o_number];
847 [o_outline_view deselectRow: i_row];
851 if( p_item->i_children == -1 )
853 playlist_PreparseEnqueue( p_playlist, &p_item->input );
857 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
861 vlc_object_release( p_playlist );
863 [self playlistUpdated];
866 - (IBAction)servicesChange:(id)sender
868 NSMenuItem *o_mi = (NSMenuItem *)sender;
869 NSString *o_string = [o_mi representedObject];
870 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
872 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
873 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
875 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
877 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
878 [o_string cString] ) ? YES : NO];
880 i_current_view = VIEW_CATEGORY;
881 playlist_ViewUpdate( p_playlist, i_current_view );
882 vlc_object_release( p_playlist );
883 [self playlistUpdated];
887 - (IBAction)selectAll:(id)sender
889 [o_outline_view selectAll: nil];
892 - (IBAction)deleteItem:(id)sender
894 int i, i_count, i_row;
895 NSMutableArray *o_to_delete;
898 playlist_t * p_playlist;
899 intf_thread_t * p_intf = VLCIntf;
901 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
904 if ( p_playlist == NULL )
908 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
909 i_count = [o_to_delete count];
911 for( i = 0; i < i_count; i++ )
913 o_number = [o_to_delete lastObject];
914 i_row = [o_number intValue];
915 id o_item = [o_outline_view itemAtRow: i_row];
916 playlist_item_t *p_item = [o_item pointerValue];
917 [o_to_delete removeObject: o_number];
918 [o_outline_view deselectRow: i_row];
920 if( [[o_outline_view dataSource] outlineView:o_outline_view
921 numberOfChildrenOfItem: o_item] > 0 )
922 //is a node and not an item
924 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
925 [self isItem: p_playlist->status.p_item inNode:
926 ((playlist_item_t *)[o_item pointerValue])
927 checkItemExistence: NO] == YES )
929 // if current item is in selected node and is playing then stop playlist
930 playlist_Stop( p_playlist );
932 vlc_mutex_lock( &p_playlist->object_lock );
933 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
934 vlc_mutex_unlock( &p_playlist->object_lock );
938 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
939 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
941 playlist_Stop( p_playlist );
943 vlc_mutex_lock( &p_playlist->object_lock );
944 playlist_Delete( p_playlist, p_item->input.i_id );
945 vlc_mutex_unlock( &p_playlist->object_lock );
948 [self playlistUpdated];
949 vlc_object_release( p_playlist );
952 - (IBAction)sortNodeByName:(id)sender
954 [self sortNode: SORT_TITLE];
957 - (IBAction)sortNodeByAuthor:(id)sender
959 [self sortNode: SORT_AUTHOR];
962 - (void)sortNode:(int)i_mode
964 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
966 playlist_item_t * p_item;
968 if (p_playlist == NULL)
973 if( [o_outline_view selectedRow] > -1 )
975 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
979 /*If no item is selected, sort the whole playlist*/
981 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
982 p_item = p_view->p_root;
985 if( p_item->i_children > -1 ) // the item is a node
987 vlc_mutex_lock( &p_playlist->object_lock );
988 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
989 vlc_mutex_unlock( &p_playlist->object_lock );
995 for( i = 0 ; i < p_item->i_parents ; i++ )
997 if( p_item->pp_parents[i]->i_view == i_current_view )
999 vlc_mutex_lock( &p_playlist->object_lock );
1000 playlist_RecursiveNodeSort( p_playlist,
1001 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
1002 vlc_mutex_unlock( &p_playlist->object_lock );
1007 vlc_object_release( p_playlist );
1008 [self playlistUpdated];
1011 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
1013 intf_thread_t * p_intf = VLCIntf;
1014 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1017 if( p_playlist == NULL )
1021 playlist_item_t *p_item;
1023 BOOL b_rem = FALSE, b_dir = FALSE;
1024 NSString *o_uri, *o_name;
1029 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1030 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1031 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1033 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1034 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1036 int i_count, i_index;
1037 struct statfs *mounts = NULL;
1039 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1040 /* getmntinfo returns a pointer to static data. Do not free. */
1041 for( i_index = 0 ; i_index < i_count; i_index++ )
1043 NSMutableString *o_temp, *o_temp2;
1044 o_temp = [NSMutableString stringWithString: o_uri];
1045 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1046 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1047 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1048 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1050 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1052 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1056 /* If no name, then make a guess */
1057 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1059 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1060 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1061 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1063 /* All of this is to make sure CD's play when you D&D them on VLC */
1064 /* Converts mountpoint to a /dev file */
1067 NSMutableString *o_temp;
1069 buf = (struct statfs *) malloc (sizeof(struct statfs));
1070 statfs( [o_uri fileSystemRepresentation], buf );
1071 psz_dev = strdup(buf->f_mntfromname);
1072 o_temp = [NSMutableString stringWithCString: psz_dev ];
1073 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1074 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1075 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1079 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1085 for( i = 0; i < (int)[o_options count]; i++ )
1087 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1091 /* Recent documents menu */
1092 o_true_file = [NSURL fileURLWithPath: o_uri];
1093 if( o_true_file != nil )
1095 [[NSDocumentController sharedDocumentController]
1096 noteNewRecentDocumentURL: o_true_file];
1099 vlc_object_release( p_playlist );
1103 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1106 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1108 if( p_playlist == NULL )
1113 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1115 playlist_item_t *p_item;
1116 NSDictionary *o_one_item;
1119 o_one_item = [o_array objectAtIndex: i_item];
1120 p_item = [self createItem: o_one_item];
1127 playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1129 if( i_item == 0 && !b_enqueue )
1131 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1134 vlc_object_release( p_playlist );
1137 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1140 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1142 if( p_playlist == NULL )
1147 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1149 playlist_item_t *p_item;
1150 NSDictionary *o_one_item;
1153 o_one_item = [o_array objectAtIndex: i_item];
1154 p_item = [self createItem: o_one_item];
1161 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
1163 if( i_item == 0 && !b_enqueue )
1165 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1168 vlc_object_release( p_playlist );
1172 - (IBAction)handlePopUp:(id)sender
1175 intf_thread_t * p_intf = VLCIntf;
1176 vlc_value_t val1,val2;
1177 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1179 if( p_playlist == NULL )
1184 switch( [o_loop_popup indexOfSelectedItem] )
1189 var_Set( p_playlist, "loop", val1 );
1191 var_Set( p_playlist, "repeat", val1 );
1192 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1197 var_Set( p_playlist, "repeat", val1 );
1199 var_Set( p_playlist, "loop", val1 );
1200 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1204 var_Get( p_playlist, "repeat", &val1 );
1205 var_Get( p_playlist, "loop", &val2 );
1206 if( val1.b_bool || val2.b_bool )
1209 var_Set( p_playlist, "repeat", val1 );
1210 var_Set( p_playlist, "loop", val1 );
1211 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1215 vlc_object_release( p_playlist );
1216 [self playlistUpdated];
1219 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1221 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1223 playlist_item_t *p_selected_item;
1224 int i_current, i_selected_row;
1229 i_selected_row = [o_outline_view selectedRow];
1230 if (i_selected_row < 0)
1233 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1234 i_selected_row] pointerValue];
1236 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1239 NSString *o_current_name, *o_current_author;
1241 vlc_mutex_lock( &p_playlist->object_lock );
1242 o_current_name = [NSString stringWithUTF8String:
1243 p_item->pp_children[i_current]->input.psz_name];
1244 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1245 _("Meta-information"),_("Artist") );
1246 o_current_author = [NSString stringWithUTF8String: psz_temp];
1248 vlc_mutex_unlock( &p_playlist->object_lock );
1250 if( p_selected_item == p_item->pp_children[i_current] &&
1251 b_selected_item_met == NO )
1253 b_selected_item_met = YES;
1255 else if( p_selected_item == p_item->pp_children[i_current] &&
1256 b_selected_item_met == YES )
1258 vlc_object_release( p_playlist );
1261 else if( b_selected_item_met == YES &&
1262 ( [o_current_name rangeOfString:[o_search_field
1263 stringValue] options:NSCaseInsensitiveSearch ].length ||
1264 [o_current_author rangeOfString:[o_search_field
1265 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1267 vlc_object_release( p_playlist );
1268 /*Adds the parent items in the result array as well, so that we can
1270 return [NSMutableArray arrayWithObject: [NSValue
1271 valueWithPointer: p_item->pp_children[i_current]]];
1273 if( p_item->pp_children[i_current]->i_children > 0 )
1275 id o_result = [self subSearchItem:
1276 p_item->pp_children[i_current]];
1277 if( o_result != NULL )
1279 vlc_object_release( p_playlist );
1280 [o_result insertObject: [NSValue valueWithPointer:
1281 p_item->pp_children[i_current]] atIndex:0];
1286 vlc_object_release( p_playlist );
1290 - (IBAction)searchItem:(id)sender
1292 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1294 playlist_view_t * p_view;
1300 b_selected_item_met = NO;
1302 if( p_playlist == NULL )
1304 p_view = playlist_ViewFind( p_playlist, i_current_view );
1308 /*First, only search after the selected item:*
1309 *(b_selected_item_met = NO) */
1310 o_result = [self subSearchItem:p_view->p_root];
1311 if( o_result == NULL )
1313 /* If the first search failed, search again from the beginning */
1314 o_result = [self subSearchItem:p_view->p_root];
1316 if( o_result != NULL )
1319 if( [[o_result objectAtIndex: 0] pointerValue] ==
1320 p_playlist->p_general )
1325 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1327 [o_outline_view expandItem: [o_outline_dict objectForKey:
1328 [NSString stringWithFormat: @"%p",
1329 [[o_result objectAtIndex: i] pointerValue]]]];
1331 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1332 [NSString stringWithFormat: @"%p",
1333 [[o_result objectAtIndex: [o_result count] - 1 ]
1338 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1339 [o_outline_view scrollRowToVisible: i_row];
1342 vlc_object_release( p_playlist );
1345 - (IBAction)recursiveExpandNode:(id)sender
1348 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1349 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1351 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1352 isItemExpandable: o_item] )
1354 for( i = 0 ; i < p_item->i_parents ; i++ )
1356 if( p_item->pp_parents[i]->i_view == i_current_view )
1358 o_item = [o_outline_dict objectForKey: [NSString
1359 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1365 /* We need to collapse the node first, since OSX refuses to recursively
1366 expand an already expanded node, even if children nodes are collapsed. */
1367 [o_outline_view collapseItem: o_item collapseChildren: YES];
1368 [o_outline_view expandItem: o_item expandChildren: YES];
1371 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1375 vlc_bool_t b_item_sel;
1377 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1379 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1380 [o_outline_view selectedRow] != -1 );
1381 b_rows = [o_outline_view numberOfRows] != 0;
1383 [o_mi_play setEnabled: b_item_sel];
1384 [o_mi_delete setEnabled: b_item_sel];
1385 [o_mi_selectall setEnabled: b_rows];
1386 [o_mi_info setEnabled: b_item_sel];
1387 [o_mi_preparse setEnabled: b_item_sel];
1388 [o_mi_recursive_expand setEnabled: b_item_sel];
1389 [o_mi_sort_name setEnabled: b_item_sel];
1390 [o_mi_sort_author setEnabled: b_item_sel];
1392 return( o_ctx_menu );
1395 - (void)outlineView: (NSTableView*)o_tv
1396 didClickTableColumn:(NSTableColumn *)o_tc
1398 int i_mode = 0, i_type;
1399 intf_thread_t *p_intf = VLCIntf;
1400 playlist_view_t *p_view;
1402 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1404 if( p_playlist == NULL )
1409 /* Check whether the selected table column header corresponds to a
1410 sortable table column*/
1411 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1413 vlc_object_release( p_playlist );
1417 p_view = playlist_ViewFind( p_playlist, i_current_view );
1419 if( o_tc_sortColumn == o_tc )
1421 b_isSortDescending = !b_isSortDescending;
1425 b_isSortDescending = VLC_FALSE;
1428 if( o_tc == o_tc_name )
1430 i_mode = SORT_TITLE;
1432 else if( o_tc == o_tc_author )
1434 i_mode = SORT_AUTHOR;
1437 if( b_isSortDescending )
1439 i_type = ORDER_REVERSE;
1443 i_type = ORDER_NORMAL;
1446 vlc_mutex_lock( &p_playlist->object_lock );
1447 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1448 vlc_mutex_unlock( &p_playlist->object_lock );
1450 vlc_object_release( p_playlist );
1451 [self playlistUpdated];
1453 o_tc_sortColumn = o_tc;
1454 [o_outline_view setHighlightedTableColumn:o_tc];
1456 if( b_isSortDescending )
1458 [o_outline_view setIndicatorImage:o_descendingSortingImage
1459 inTableColumn:o_tc];
1463 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1464 inTableColumn:o_tc];
1469 - (void)outlineView:(NSOutlineView *)outlineView
1470 willDisplayCell:(id)cell
1471 forTableColumn:(NSTableColumn *)tableColumn
1474 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1479 if( !p_playlist ) return;
1481 o_playing_item = [o_outline_dict objectForKey:
1482 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1484 if( [self isItem: [o_playing_item pointerValue] inNode:
1485 [item pointerValue] checkItemExistence: YES]
1486 || [o_playing_item isEqual: item] )
1488 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1492 [cell setFont: [NSFont systemFontOfSize: 0]];
1494 vlc_object_release( p_playlist );
1499 @implementation VLCPlaylist (NSOutlineViewDataSource)
1501 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1503 id o_value = [super outlineView: outlineView child: index ofItem: item];
1504 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1507 if( !p_playlist ) return nil;
1509 if( p_playlist->i_size >= 2 )
1511 [o_status_field setStringValue: [NSString stringWithFormat:
1512 _NS("%i items in playlist"), p_playlist->i_size]];
1516 if( p_playlist->i_size == 0 )
1518 [o_status_field setStringValue: [NSString stringWithFormat:
1519 _NS("no items in playlist"), p_playlist->i_size]];
1523 [o_status_field setStringValue: [NSString stringWithFormat:
1524 _NS("1 item in playlist"), p_playlist->i_size]];
1527 vlc_object_release( p_playlist );
1529 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1530 [o_value pointerValue]]];
1536 /* Required for drag & drop and reordering */
1537 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1540 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1543 /* First remove the items that were moved during the last drag & drop
1545 [o_items_array removeAllObjects];
1546 [o_nodes_array removeAllObjects];
1548 if( !p_playlist ) return NO;
1550 for( i = 0 ; i < [items count] ; i++ )
1552 id o_item = [items objectAtIndex: i];
1554 /* Refuse to move items that are not in the General Node
1555 (Service Discovery) */
1556 if( ![self isItem: [o_item pointerValue] inNode:
1557 p_playlist->p_general checkItemExistence: NO])
1559 vlc_object_release(p_playlist);
1562 /* Fill the items and nodes to move in 2 different arrays */
1563 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1564 [o_nodes_array addObject: o_item];
1566 [o_items_array addObject: o_item];
1569 /* Now we need to check if there are selected items that are in already
1570 selected nodes. In that case, we only want to move the nodes */
1571 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1572 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1574 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1575 a Drop operation coming from the playlist. */
1577 [pboard declareTypes: [NSArray arrayWithObjects:
1578 @"VLCPlaylistItemPboardType", nil] owner: self];
1579 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1581 vlc_object_release(p_playlist);
1585 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1587 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1589 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1591 if( !p_playlist ) return NSDragOperationNone;
1593 /* Dropping ON items is not allowed if item is not a node */
1596 if( index == NSOutlineViewDropOnItemIndex &&
1597 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1599 vlc_object_release( p_playlist );
1600 return NSDragOperationNone;
1604 /* We refuse to drop an item in anything else than a child of the General
1605 Node. We still accept items that would be root nodes of the outlineview
1606 however, to allow drop in an empty playlist. */
1607 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1608 checkItemExistence: NO] || item == nil) )
1610 vlc_object_release( p_playlist );
1611 return NSDragOperationNone;
1614 /* Drop from the Playlist */
1615 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1618 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1620 /* We refuse to Drop in a child of an item we are moving */
1621 if( [self isItem: [item pointerValue] inNode:
1622 [[o_nodes_array objectAtIndex: i] pointerValue]
1623 checkItemExistence: NO] )
1625 vlc_object_release( p_playlist );
1626 return NSDragOperationNone;
1629 vlc_object_release(p_playlist);
1630 return NSDragOperationMove;
1633 /* Drop from the Finder */
1634 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1636 vlc_object_release(p_playlist);
1637 return NSDragOperationGeneric;
1639 vlc_object_release(p_playlist);
1640 return NSDragOperationNone;
1643 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1645 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1647 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1649 if( !p_playlist ) return NO;
1651 /* Drag & Drop inside the playlist */
1652 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1656 playlist_item_t *p_new_parent, *p_item = NULL;
1657 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1659 /* If the item is to be dropped as root item of the outline, make it a
1660 child of the General node.
1661 Else, choose the proposed parent as parent. */
1663 p_new_parent = p_playlist->p_general;
1665 p_new_parent = [item pointerValue];
1667 /* If the proposed parent is not a node, then use the parent node of
1669 if( p_new_parent->i_children <= 0 )
1672 playlist_item_t *p_temp_item = p_new_parent;
1673 p_new_parent = [self parentOfItem: p_new_parent];
1676 vlc_object_release(p_playlist);
1679 /* Calculate the position of the dropped item in this new parent:
1680 following the first proposed parent. */
1681 for( j = 0; j < p_new_parent->i_children; j++ )
1683 if( p_new_parent->pp_children[j] == p_temp_item )
1688 else if( j == p_new_parent->i_children - 1 )
1693 for( i = 0; i < [o_all_items count]; i++ )
1695 playlist_item_t *p_old_parent = NULL;
1696 int i_old_index = 0;
1698 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1699 p_old_parent = [self parentOfItem: p_item];
1702 /* We may need the old index later */
1703 if( p_new_parent == p_old_parent )
1706 for( j = 0; j < p_old_parent->i_children; j++ )
1708 if( p_old_parent->pp_children[j] == p_item )
1717 /* If we move the playing item in a different node or we move the
1718 node containing the playing item in a different node, then stop
1719 playback, or the playlist refuses to detach the item. */
1721 vlc_mutex_lock( &p_playlist->object_lock );
1722 // Acually detach the item from the old position
1723 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1725 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1729 /* Calculate the new index */
1732 /* If we move the item in the same node, we need to take into
1733 account that one item will be deleted */
1734 else if((p_new_parent == p_old_parent &&
1735 i_old_index < index + (int)i)
1736 || p_new_parent == p_playlist->p_general || index == 0 )
1737 i_new_index = index + i;
1739 i_new_index = index + i + 1;
1740 // Reattach the item to the new position
1741 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1742 p_new_parent, i_new_index );
1744 vlc_mutex_unlock( &p_playlist->object_lock );
1746 [self playlistUpdated];
1747 i_row = [o_outline_view rowForItem:[o_outline_dict
1748 objectForKey:[NSString stringWithFormat: @"%p",
1749 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1753 i_row = [o_outline_view rowForItem:[o_outline_dict
1754 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1757 [o_outline_view deselectAll: self];
1758 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1759 [o_outline_view scrollRowToVisible: i_row];
1761 vlc_object_release(p_playlist);
1765 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1768 playlist_item_t *p_node = [item pointerValue];
1770 NSArray *o_array = [NSArray array];
1771 NSArray *o_values = [[o_pasteboard propertyListForType:
1772 NSFilenamesPboardType]
1773 sortedArrayUsingSelector:
1774 @selector(caseInsensitiveCompare:)];
1776 for( i = 0; i < (int)[o_values count]; i++)
1778 NSDictionary *o_dic;
1779 o_dic = [NSDictionary dictionaryWithObject:[o_values
1780 objectAtIndex:i] forKey:@"ITEM_URL"];
1781 o_array = [o_array arrayByAddingObject: o_dic];
1786 [self appendArray: o_array atPos: index enqueue: YES];
1788 else if( p_node->i_children == -1 )
1791 playlist_item_t *p_real_node = NULL;
1793 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1795 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1797 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1800 if( i_counter == p_node->i_parents )
1802 vlc_object_release(p_playlist);
1806 [self appendNodeArray: o_array inNode: p_real_node
1807 atPos: index inView: i_current_view enqueue: YES];
1811 [self appendNodeArray: o_array inNode: p_node
1812 atPos: index inView: i_current_view enqueue: YES];
1814 vlc_object_release( p_playlist );
1817 vlc_object_release( p_playlist );
1821 /* Delegate method of NSWindow */
1822 /*- (void)windowWillClose:(NSNotification *)aNotification
1824 [o_btn_playlist setState: NSOffState];