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>
53 /*****************************************************************************
54 * VLCPlaylistView implementation
55 *****************************************************************************/
56 @implementation VLCPlaylistView
58 - (NSMenu *)menuForEvent:(NSEvent *)o_event
60 return( [[self delegate] menuForEvent: o_event] );
63 - (void)keyDown:(NSEvent *)o_event
67 if( [[o_event characters] length] )
69 key = [[o_event characters] characterAtIndex: 0];
74 case NSDeleteCharacter:
75 case NSDeleteFunctionKey:
76 case NSDeleteCharFunctionKey:
77 case NSBackspaceCharacter:
78 [[self delegate] deleteItem:self];
81 case NSEnterCharacter:
82 case NSCarriageReturnCharacter:
83 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
88 [super keyDown: o_event];
96 /*****************************************************************************
97 * VLCPlaylistCommon implementation
99 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
100 * It contains the common methods and elements of these 2 entities.
101 *****************************************************************************/
102 @implementation VLCPlaylistCommon
106 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
108 i_current_view = VIEW_CATEGORY;
109 playlist_ViewUpdate( p_playlist, i_current_view );
111 [o_outline_view setTarget: self];
112 [o_outline_view setDelegate: self];
113 [o_outline_view setDataSource: self];
115 vlc_object_release( p_playlist );
121 [[o_tc_name headerCell] setStringValue:_NS("Name")];
122 [[o_tc_author headerCell] setStringValue:_NS("Author")];
123 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
126 - (NSOutlineView *)outlineView
128 return o_outline_view;
131 - (playlist_item_t *)selectedPlaylistItem
133 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
139 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
141 /* return the number of children for Obj-C pointer item */ /* DONE */
142 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
145 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
147 if( p_playlist == NULL || outlineView != o_outline_view )
153 playlist_view_t *p_view;
154 p_view = playlist_ViewFind( p_playlist, i_current_view );
155 if( p_view && p_view->p_root )
157 i_return = p_view->p_root->i_children;
158 if( i_current_view == VIEW_CATEGORY )
160 i_return--; /* remove the GENERAL item from the list */
161 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
167 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
169 i_return = p_item->i_children;
171 vlc_object_release( p_playlist );
179 /* return the child at index for the Obj-C pointer item */ /* DONE */
180 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
182 playlist_item_t *p_return = NULL;
183 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
187 if( p_playlist == NULL )
193 playlist_view_t *p_view;
194 p_view = playlist_ViewFind( p_playlist, i_current_view );
195 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
197 if( i_current_view == VIEW_CATEGORY )
199 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
201 p_return = p_playlist->p_general->pp_children[index];
203 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
205 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
211 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
212 if( p_item && index < p_item->i_children && index >= 0 )
213 p_return = p_item->pp_children[index];
217 vlc_object_release( p_playlist );
219 o_value = [[NSValue valueWithPointer: p_return] retain];
224 /* is the item expandable */
225 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
228 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
230 if( p_playlist == NULL )
236 playlist_view_t *p_view;
237 p_view = playlist_ViewFind( p_playlist, i_current_view );
238 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
240 if( i_current_view == VIEW_CATEGORY )
243 i_return += p_playlist->p_general->i_children;
248 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
250 i_return = p_item->i_children;
252 vlc_object_release( p_playlist );
260 /* retrieve the string values for the cells */
261 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
264 intf_thread_t *p_intf = VLCIntf;
265 playlist_t *p_playlist;
266 playlist_item_t *p_item;
268 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
270 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
272 if( p_playlist == NULL )
277 p_item = (playlist_item_t *)[item pointerValue];
281 vlc_object_release( p_playlist );
285 if( [[o_tc identifier] isEqualToString:@"1"] )
287 o_value = [NSString stringWithUTF8String:
288 p_item->input.psz_name];
289 if( o_value == NULL )
290 o_value = [NSString stringWithCString:
291 p_item->input.psz_name];
293 else if( [[o_tc identifier] isEqualToString:@"2"] )
296 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
298 if( psz_temp == NULL )
302 o_value = [NSString stringWithUTF8String: psz_temp];
303 if( o_value == NULL )
305 o_value = [NSString stringWithCString: psz_temp];
310 else if( [[o_tc identifier] isEqualToString:@"3"] )
312 char psz_duration[MSTRTIME_MAX_SIZE];
313 mtime_t dur = p_item->input.i_duration;
316 secstotimestr( psz_duration, dur/1000000 );
317 o_value = [NSString stringWithUTF8String: psz_duration];
321 o_value = @"-:--:--";
324 vlc_object_release( p_playlist );
331 /*****************************************************************************
332 * VLCPlaylistWizard implementation
333 *****************************************************************************/
334 @implementation VLCPlaylistWizard
336 - (IBAction)reloadOutlineView
338 [o_outline_view reloadData];
343 /*****************************************************************************
344 * VLCPlaylist implementation
345 *****************************************************************************/
346 @implementation VLCPlaylist
353 o_outline_dict = [[NSMutableDictionary alloc] init];
354 o_nodes_array = [[NSMutableArray alloc] init];
355 o_items_array = [[NSMutableArray alloc] init];
365 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
367 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
372 [super awakeFromNib];
374 [o_outline_view setDoubleAction: @selector(playItem:)];
376 [o_outline_view registerForDraggedTypes:
377 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
378 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
380 /* We need to check whether _defaultTableHeaderSortImage exists, since it
381 belongs to an Apple hidden private API, and then can "disapear" at any time*/
383 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
385 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
389 o_ascendingSortingImage = nil;
392 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
394 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
398 o_descendingSortingImage = nil;
401 o_tc_sortColumn = nil;
403 for( i_index = 0; i_index < p_list->i_count; i_index++ )
406 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
408 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
410 /* create the menu entries used in the playlist menu */
411 o_lmi = [[o_mi_services submenu] addItemWithTitle:
412 [NSString stringWithUTF8String:
413 p_parser->psz_longname ? p_parser->psz_longname :
414 ( p_parser->psz_shortname ? p_parser->psz_shortname:
415 p_parser->psz_object_name)]
416 action: @selector(servicesChange:)
418 [o_lmi setTarget: self];
419 [o_lmi setRepresentedObject:
420 [NSString stringWithCString: p_parser->psz_object_name]];
421 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
422 p_parser->psz_object_name ) )
423 [o_lmi setState: NSOnState];
425 /* create the menu entries for the main menu */
426 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
427 [NSString stringWithUTF8String:
428 p_parser->psz_longname ? p_parser->psz_longname :
429 ( p_parser->psz_shortname ? p_parser->psz_shortname:
430 p_parser->psz_object_name)]
431 action: @selector(servicesChange:)
433 [o_lmi setTarget: self];
434 [o_lmi setRepresentedObject:
435 [NSString stringWithCString: p_parser->psz_object_name]];
436 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
437 p_parser->psz_object_name ) )
438 [o_lmi setState: NSOnState];
441 vlc_list_release( p_list );
442 vlc_object_release( p_playlist );
444 /* Change the simple textfield into a searchField if we can... */
446 if( MACOS_VERSION >= 10.3 )
448 NSView *o_parentview = [o_status_field superview];
449 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
450 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
451 [o_better_search_field setDelegate:self];
452 [[NSNotificationCenter defaultCenter] addObserver: self
453 selector: @selector(searchfieldChanged:)
454 name: NSControlTextDidChangeNotification
455 object: o_better_search_field];
457 [o_better_search_field setTarget:self];
458 [o_better_search_field setAction:@selector(searchItem:)];
460 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
461 [o_parentview addSubview:o_better_search_field];
462 [o_search_field setHidden:YES];
465 //[self playlistUpdated];
468 - (void)searchfieldChanged:(NSNotification *)o_notification
470 [o_search_field setStringValue:[[o_notification object] stringValue]];
477 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
478 [o_mi_play setTitle: _NS("Play")];
479 [o_mi_delete setTitle: _NS("Delete")];
480 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
481 [o_mi_selectall setTitle: _NS("Select All")];
482 [o_mi_info setTitle: _NS("Properties")];
483 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
484 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
485 [o_mi_services setTitle: _NS("Services discovery")];
486 [o_status_field setStringValue: [NSString stringWithFormat:
487 _NS("no items in playlist")]];
489 [o_random_ckb setTitle: _NS("Random")];
491 [o_search_button setTitle: _NS("Search")];
493 [o_search_field setToolTip: _NS("Search in Playlist")];
494 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
495 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
496 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
499 - (void)playlistUpdated
503 /* Clear indications of any existing column sorting*/
504 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
506 [o_outline_view setIndicatorImage:nil inTableColumn:
507 [[o_outline_view tableColumns] objectAtIndex:i]];
510 [o_outline_view setHighlightedTableColumn:nil];
511 o_tc_sortColumn = nil;
512 // TODO Find a way to keep the dict size to a minimum
513 //[o_outline_dict removeAllObjects];
514 [o_outline_view reloadData];
517 - (void)playModeUpdated
519 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
521 vlc_value_t val, val2;
523 if( p_playlist == NULL )
528 var_Get( p_playlist, "loop", &val2 );
529 var_Get( p_playlist, "repeat", &val );
530 if( val.b_bool == VLC_TRUE )
532 [o_loop_popup selectItemAtIndex: 1];
534 else if( val2.b_bool == VLC_TRUE )
536 [o_loop_popup selectItemAtIndex: 2];
540 [o_loop_popup selectItemAtIndex: 0];
543 var_Get( p_playlist, "random", &val );
544 [o_random_ckb setState: val.b_bool];
546 vlc_object_release( p_playlist );
549 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
552 for( i = 0 ; i < p_item->i_parents; i++ )
554 if( p_item->pp_parents[i]->i_view == i_current_view )
556 return p_item->pp_parents[i]->p_parent;
562 - (void)updateRowSelection
568 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
570 playlist_item_t *p_item, *p_temp_item;
571 NSMutableArray *o_array = [NSMutableArray array];
573 if( p_playlist == NULL )
576 p_item = p_playlist->status.p_item;
579 vlc_object_release(p_playlist);
583 p_temp_item = p_item;
584 while( p_temp_item->i_parents > 0 )
586 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
588 p_temp_item = [self parentOfItem: p_temp_item];
589 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
591 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
593 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
599 for (j = 0 ; j < [o_array count] - 1 ; j++)
602 if( ( o_item = [o_outline_dict objectForKey:
603 [NSString stringWithFormat: @"%p",
604 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
605 [o_outline_view expandItem: o_item];
609 i_row = [o_outline_view rowForItem:[o_outline_dict
610 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
612 [o_outline_view selectRow: i_row byExtendingSelection: NO];
613 [o_outline_view scrollRowToVisible: i_row];
615 vlc_object_release(p_playlist);
618 /* Check if p_item is a child of p_node recursively. We need to check the item existence first since OSX sometimes tries to redraw items that have been
619 deleted. We don't do it when not required since this verification takes
620 quite a long time on big playlists (yes, pretty hacky). */
621 - (BOOL)isItem: (playlist_item_t *)p_item
622 inNode: (playlist_item_t *)p_node
623 checkItemExistence:(BOOL)b_check
626 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
628 playlist_item_t *p_temp_item = p_item;
630 if( p_playlist == NULL )
635 if( p_node == p_item )
637 vlc_object_release(p_playlist);
641 if( p_node->i_children < 1)
643 vlc_object_release(p_playlist);
650 vlc_mutex_lock( &p_playlist->object_lock );
654 /* Since outlineView: willDisplayCell:... may call this function with
655 p_items that don't exist anymore, first check if the item is still
656 in the playlist. Any cleaner solution welcomed. */
657 for( i = 0; i < p_playlist->i_all_size; i++ )
659 if( p_playlist->pp_all_items[i] == p_item ) break;
660 else if ( i == p_playlist->i_all_size - 1 )
662 vlc_object_release( p_playlist );
663 vlc_mutex_unlock( &p_playlist->object_lock );
669 while( p_temp_item->i_parents > 0 )
671 p_temp_item = [self parentOfItem: p_temp_item];
672 if( p_temp_item == p_node )
674 vlc_mutex_unlock( &p_playlist->object_lock );
675 vlc_object_release( p_playlist );
679 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
681 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
683 if( p_temp_item->pp_parents[i]->p_parent == p_node )
685 vlc_mutex_unlock( &p_playlist->object_lock );
686 vlc_object_release( p_playlist );
691 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
697 vlc_mutex_unlock( &p_playlist->object_lock );
700 vlc_object_release( p_playlist );
704 /* This method is usefull for instance to remove the selected children of an
705 already selected node */
706 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
709 for( i = 0 ; i < [o_items count] ; i++ )
711 for ( j = 0 ; j < [o_nodes count] ; j++ )
713 if( o_items == o_nodes)
715 if( j == i ) continue;
717 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
718 inNode: [[o_nodes objectAtIndex:j] pointerValue]
719 checkItemExistence: NO] )
721 [o_items removeObjectAtIndex:i];
722 /* We need to execute the next iteration with the same index
723 since the current item has been deleted */
732 - (IBAction)savePlaylist:(id)sender
734 intf_thread_t * p_intf = VLCIntf;
735 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
738 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
739 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
740 [o_save_panel setTitle: _NS("Save Playlist")];
741 [o_save_panel setPrompt: _NS("Save")];
743 if( [o_save_panel runModalForDirectory: nil
744 file: o_name] == NSOKButton )
746 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
751 /* When called retrieves the selected outlineview row and plays that node or item */
752 - (IBAction)playItem:(id)sender
754 intf_thread_t * p_intf = VLCIntf;
755 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
758 if( p_playlist != NULL )
760 playlist_item_t *p_item;
761 playlist_item_t *p_node = NULL;
764 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
768 if( p_item->i_children == -1 )
770 p_node = [self parentOfItem: p_item];
772 /* for( i = 0 ; i < p_item->i_parents ; i++ )
774 if( p_item->pp_parents[i]->i_view == i_current_view )
776 p_node = p_item->pp_parents[i]->p_parent;
783 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
785 p_item = p_node->pp_children[0];
792 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
794 vlc_object_release( p_playlist );
798 - (IBAction)servicesChange:(id)sender
800 NSMenuItem *o_mi = (NSMenuItem *)sender;
801 NSString *o_string = [o_mi representedObject];
802 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
804 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
805 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
807 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
809 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
810 [o_string cString] ) ? YES : NO];
812 i_current_view = VIEW_CATEGORY;
813 playlist_ViewUpdate( p_playlist, i_current_view );
814 vlc_object_release( p_playlist );
815 [self playlistUpdated];
819 - (IBAction)selectAll:(id)sender
821 [o_outline_view selectAll: nil];
824 - (IBAction)deleteItem:(id)sender
826 int i, i_count, i_row;
827 NSMutableArray *o_to_delete;
830 playlist_t * p_playlist;
831 intf_thread_t * p_intf = VLCIntf;
833 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
836 if ( p_playlist == NULL )
840 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
841 i_count = [o_to_delete count];
843 for( i = 0; i < i_count; i++ )
845 o_number = [o_to_delete lastObject];
846 i_row = [o_number intValue];
847 id o_item = [o_outline_view itemAtRow: i_row];
848 playlist_item_t *p_item = [o_item pointerValue];
849 [o_to_delete removeObject: o_number];
850 [o_outline_view deselectRow: i_row];
852 if( [[o_outline_view dataSource] outlineView:o_outline_view
853 numberOfChildrenOfItem: o_item] > 0 )
854 //is a node and not an item
856 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
857 [self isItem: p_playlist->status.p_item inNode:
858 ((playlist_item_t *)[o_item pointerValue])
859 checkItemExistence: NO] == YES )
861 // if current item is in selected node and is playing then stop playlist
862 playlist_Stop( p_playlist );
864 vlc_mutex_lock( &p_playlist->object_lock );
865 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
866 vlc_mutex_unlock( &p_playlist->object_lock );
870 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
871 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
873 playlist_Stop( p_playlist );
875 vlc_mutex_lock( &p_playlist->object_lock );
876 playlist_Delete( p_playlist, p_item->input.i_id );
877 vlc_mutex_unlock( &p_playlist->object_lock );
880 [self playlistUpdated];
881 vlc_object_release( p_playlist );
884 - (IBAction)sortNodeByName:(id)sender
886 [self sortNode: SORT_TITLE];
889 - (IBAction)sortNodeByAuthor:(id)sender
891 [self sortNode: SORT_AUTHOR];
894 - (void)sortNode:(int)i_mode
896 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
898 playlist_item_t * p_item;
900 if (p_playlist == NULL)
905 if( [o_outline_view selectedRow] > -1 )
907 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
911 /*If no item is selected, sort the whole playlist*/
913 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
914 p_item = p_view->p_root;
917 if( p_item->i_children > -1 ) // the item is a node
919 vlc_mutex_lock( &p_playlist->object_lock );
920 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
921 vlc_mutex_unlock( &p_playlist->object_lock );
927 for( i = 0 ; i < p_item->i_parents ; i++ )
929 if( p_item->pp_parents[i]->i_view == i_current_view )
931 vlc_mutex_lock( &p_playlist->object_lock );
932 playlist_RecursiveNodeSort( p_playlist,
933 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
934 vlc_mutex_unlock( &p_playlist->object_lock );
939 vlc_object_release( p_playlist );
940 [self playlistUpdated];
943 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
945 intf_thread_t * p_intf = VLCIntf;
946 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
949 if( p_playlist == NULL )
953 playlist_item_t *p_item;
955 BOOL b_rem = FALSE, b_dir = FALSE;
956 NSString *o_uri, *o_name;
961 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
962 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
963 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
965 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
966 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
968 int i_count, i_index;
969 struct statfs *mounts = NULL;
971 i_count = getmntinfo (&mounts, MNT_NOWAIT);
972 /* getmntinfo returns a pointer to static data. Do not free. */
973 for( i_index = 0 ; i_index < i_count; i_index++ )
975 NSMutableString *o_temp, *o_temp2;
976 o_temp = [NSMutableString stringWithString: o_uri];
977 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
978 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
979 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
980 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
982 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
984 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
988 /* If no name, then make a guess */
989 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
991 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
992 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
993 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
995 /* All of this is to make sure CD's play when you D&D them on VLC */
996 /* Converts mountpoint to a /dev file */
999 NSMutableString *o_temp;
1001 buf = (struct statfs *) malloc (sizeof(struct statfs));
1002 statfs( [o_uri fileSystemRepresentation], buf );
1003 psz_dev = strdup(buf->f_mntfromname);
1004 o_temp = [NSMutableString stringWithCString: psz_dev ];
1005 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1006 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1007 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1011 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1017 for( i = 0; i < (int)[o_options count]; i++ )
1019 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1023 /* Recent documents menu */
1024 o_true_file = [NSURL fileURLWithPath: o_uri];
1025 if( o_true_file != nil )
1027 [[NSDocumentController sharedDocumentController]
1028 noteNewRecentDocumentURL: o_true_file];
1031 vlc_object_release( p_playlist );
1035 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1038 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1040 if( p_playlist == NULL )
1045 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1047 playlist_item_t *p_item;
1048 NSDictionary *o_one_item;
1051 o_one_item = [o_array objectAtIndex: i_item];
1052 p_item = [self createItem: o_one_item];
1059 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1061 if( i_item == 0 && !b_enqueue )
1063 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1066 vlc_object_release( p_playlist );
1069 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1072 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1074 if( p_playlist == NULL )
1079 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1081 playlist_item_t *p_item;
1082 NSDictionary *o_one_item;
1085 o_one_item = [o_array objectAtIndex: i_item];
1086 p_item = [self createItem: o_one_item];
1093 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1095 if( i_item == 0 && !b_enqueue )
1097 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1100 vlc_object_release( p_playlist );
1104 - (IBAction)handlePopUp:(id)sender
1107 intf_thread_t * p_intf = VLCIntf;
1108 vlc_value_t val1,val2;
1109 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1111 if( p_playlist == NULL )
1116 switch( [o_loop_popup indexOfSelectedItem] )
1121 var_Set( p_playlist, "loop", val1 );
1123 var_Set( p_playlist, "repeat", val1 );
1124 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1129 var_Set( p_playlist, "repeat", val1 );
1131 var_Set( p_playlist, "loop", val1 );
1132 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1136 var_Get( p_playlist, "repeat", &val1 );
1137 var_Get( p_playlist, "loop", &val2 );
1138 if( val1.b_bool || val2.b_bool )
1141 var_Set( p_playlist, "repeat", val1 );
1142 var_Set( p_playlist, "loop", val1 );
1143 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1147 vlc_object_release( p_playlist );
1148 [self playlistUpdated];
1151 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1153 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1155 playlist_item_t *p_selected_item;
1156 int i_current, i_selected_row;
1161 i_selected_row = [o_outline_view selectedRow];
1162 if (i_selected_row < 0)
1165 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1166 i_selected_row] pointerValue];
1168 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1171 NSString *o_current_name, *o_current_author;
1173 vlc_mutex_lock( &p_playlist->object_lock );
1174 o_current_name = [NSString stringWithUTF8String:
1175 p_item->pp_children[i_current]->input.psz_name];
1176 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1177 _("Meta-information"),_("Artist") );
1178 o_current_author = [NSString stringWithUTF8String: psz_temp];
1180 vlc_mutex_unlock( &p_playlist->object_lock );
1182 if( p_selected_item == p_item->pp_children[i_current] &&
1183 b_selected_item_met == NO )
1185 b_selected_item_met = YES;
1187 else if( p_selected_item == p_item->pp_children[i_current] &&
1188 b_selected_item_met == YES )
1190 vlc_object_release( p_playlist );
1193 else if( b_selected_item_met == YES &&
1194 ( [o_current_name rangeOfString:[o_search_field
1195 stringValue] options:NSCaseInsensitiveSearch ].length ||
1196 [o_current_author rangeOfString:[o_search_field
1197 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1199 vlc_object_release( p_playlist );
1200 /*Adds the parent items in the result array as well, so that we can
1202 return [NSMutableArray arrayWithObject: [NSValue
1203 valueWithPointer: p_item->pp_children[i_current]]];
1205 if( p_item->pp_children[i_current]->i_children > 0 )
1207 id o_result = [self subSearchItem:
1208 p_item->pp_children[i_current]];
1209 if( o_result != NULL )
1211 vlc_object_release( p_playlist );
1212 [o_result insertObject: [NSValue valueWithPointer:
1213 p_item->pp_children[i_current]] atIndex:0];
1218 vlc_object_release( p_playlist );
1222 - (IBAction)searchItem:(id)sender
1224 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1226 playlist_view_t * p_view;
1232 b_selected_item_met = NO;
1234 if( p_playlist == NULL )
1236 p_view = playlist_ViewFind( p_playlist, i_current_view );
1240 /*First, only search after the selected item:*
1241 *(b_selected_item_met = NO) */
1242 o_result = [self subSearchItem:p_view->p_root];
1243 if( o_result == NULL )
1245 /* If the first search failed, search again from the beginning */
1246 o_result = [self subSearchItem:p_view->p_root];
1248 if( o_result != NULL )
1251 if( [[o_result objectAtIndex: 0] pointerValue] ==
1252 p_playlist->p_general )
1257 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1259 [o_outline_view expandItem: [o_outline_dict objectForKey:
1260 [NSString stringWithFormat: @"%p",
1261 [[o_result objectAtIndex: i] pointerValue]]]];
1263 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1264 [NSString stringWithFormat: @"%p",
1265 [[o_result objectAtIndex: [o_result count] - 1 ]
1270 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1271 [o_outline_view scrollRowToVisible: i_row];
1274 vlc_object_release( p_playlist );
1277 - (IBAction)recursiveExpandNode:(id)sender
1280 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1281 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1283 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1284 isItemExpandable: o_item] )
1286 for( i = 0 ; i < p_item->i_parents ; i++ )
1288 if( p_item->pp_parents[i]->i_view == i_current_view )
1290 o_item = [o_outline_dict objectForKey: [NSString
1291 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1297 /* We need to collapse the node first, since OSX refuses to recursively
1298 expand an already expanded node, even if children nodes are collapsed. */
1299 [o_outline_view collapseItem: o_item collapseChildren: YES];
1300 [o_outline_view expandItem: o_item expandChildren: YES];
1303 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1307 vlc_bool_t b_item_sel;
1309 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1311 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1312 [o_outline_view selectedRow] != -1 );
1313 b_rows = [o_outline_view numberOfRows] != 0;
1315 [o_mi_play setEnabled: b_item_sel];
1316 [o_mi_delete setEnabled: b_item_sel];
1317 [o_mi_selectall setEnabled: b_rows];
1318 [o_mi_info setEnabled: b_item_sel];
1319 [o_mi_recursive_expand setEnabled: b_item_sel];
1320 [o_mi_sort_name setEnabled: b_item_sel];
1321 [o_mi_sort_author setEnabled: b_item_sel];
1323 return( o_ctx_menu );
1326 - (void)outlineView: (NSTableView*)o_tv
1327 didClickTableColumn:(NSTableColumn *)o_tc
1329 int i_mode = 0, i_type;
1330 intf_thread_t *p_intf = VLCIntf;
1331 playlist_view_t *p_view;
1333 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1335 if( p_playlist == NULL )
1340 /* Check whether the selected table column header corresponds to a
1341 sortable table column*/
1342 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1344 vlc_object_release( p_playlist );
1348 p_view = playlist_ViewFind( p_playlist, i_current_view );
1350 if( o_tc_sortColumn == o_tc )
1352 b_isSortDescending = !b_isSortDescending;
1356 b_isSortDescending = VLC_FALSE;
1359 if( o_tc == o_tc_name )
1361 i_mode = SORT_TITLE;
1363 else if( o_tc == o_tc_author )
1365 i_mode = SORT_AUTHOR;
1368 if( b_isSortDescending )
1370 i_type = ORDER_REVERSE;
1374 i_type = ORDER_NORMAL;
1377 vlc_mutex_lock( &p_playlist->object_lock );
1378 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1379 vlc_mutex_unlock( &p_playlist->object_lock );
1381 vlc_object_release( p_playlist );
1382 [self playlistUpdated];
1384 o_tc_sortColumn = o_tc;
1385 [o_outline_view setHighlightedTableColumn:o_tc];
1387 if( b_isSortDescending )
1389 [o_outline_view setIndicatorImage:o_descendingSortingImage
1390 inTableColumn:o_tc];
1394 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1395 inTableColumn:o_tc];
1400 - (void)outlineView:(NSOutlineView *)outlineView
1401 willDisplayCell:(id)cell
1402 forTableColumn:(NSTableColumn *)tableColumn
1405 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1410 if( !p_playlist ) return;
1412 o_playing_item = [o_outline_dict objectForKey:
1413 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1415 if( [self isItem: [o_playing_item pointerValue] inNode:
1416 [item pointerValue] checkItemExistence: YES]
1417 || [o_playing_item isEqual: item] )
1419 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1423 [cell setFont: [NSFont systemFontOfSize: 0]];
1425 vlc_object_release( p_playlist );
1430 @implementation VLCPlaylist (NSOutlineViewDataSource)
1432 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1434 id o_value = [super outlineView: outlineView child: index ofItem: item];
1435 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1438 if( !p_playlist ) return nil;
1440 if( p_playlist->i_size >= 2 )
1442 [o_status_field setStringValue: [NSString stringWithFormat:
1443 _NS("%i items in playlist"), p_playlist->i_size]];
1447 if( p_playlist->i_size == 0 )
1449 [o_status_field setStringValue: [NSString stringWithFormat:
1450 _NS("no items in playlist"), p_playlist->i_size]];
1454 [o_status_field setStringValue: [NSString stringWithFormat:
1455 _NS("1 item in playlist"), p_playlist->i_size]];
1458 vlc_object_release( p_playlist );
1460 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1461 [o_value pointerValue]]];
1467 /* Required for drag & drop and reordering */
1468 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1471 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1474 /* First remove the items that were moved during the last drag & drop
1476 [o_items_array removeAllObjects];
1477 [o_nodes_array removeAllObjects];
1479 if( !p_playlist ) return NO;
1481 for( i = 0 ; i < [items count] ; i++ )
1483 id o_item = [items objectAtIndex: i];
1485 /* Refuse to move items that are not in the General Node
1486 (Service Discovery) */
1487 if( ![self isItem: [o_item pointerValue] inNode:
1488 p_playlist->p_general checkItemExistence: NO])
1490 vlc_object_release(p_playlist);
1493 /* Fill the items and nodes to move in 2 different arrays */
1494 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1495 [o_nodes_array addObject: o_item];
1497 [o_items_array addObject: o_item];
1500 /* Now we need to check if there are selected items that are in already
1501 selected nodes. In that case, we only want to move the nodes */
1502 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1503 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1506 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1508 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1510 if( j == i ) continue;
1511 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1512 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1514 [o_nodes_array removeObjectAtIndex:i];
1515 /* We need to execute the next iteration with the same index
1516 since the current item has been deleted */
1523 for( i = 0 ; i < [o_items_array count] ; i++ )
1525 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1527 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1528 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1530 [o_items_array removeObjectAtIndex:i];
1537 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1538 a Drop operation comçing from the playlist.
1539 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1542 [pboard declareTypes: [NSArray arrayWithObjects:
1543 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1544 [pboard setPropertyList:[NSArray array]
1545 forType:NSFilenamesPboardType];
1547 vlc_object_release(p_playlist);
1551 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1553 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1555 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1557 if( !p_playlist ) return NSDragOperationNone;
1559 /* We refuse to drop an item in anything else than a child of the General
1560 Node. We still accept items that would be root nodes of the outlineview
1561 however, to allow drop in an empty playlist.*/
1562 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1563 checkItemExistence: NO] || item == nil) )
1565 vlc_object_release(p_playlist);
1566 return NSDragOperationNone;
1569 /* Drop from the Playlist */
1570 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1573 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1575 /* We refuse to Drop in a child of an item we are moving */
1576 if( [self isItem: [item pointerValue] inNode:
1577 [[o_nodes_array objectAtIndex: i] pointerValue]
1578 checkItemExistence: NO] )
1580 vlc_object_release(p_playlist);
1581 return NSDragOperationNone;
1584 vlc_object_release(p_playlist);
1585 return NSDragOperationMove;
1588 /* Drop from the Finder */
1589 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1591 vlc_object_release(p_playlist);
1592 return NSDragOperationGeneric;
1594 vlc_object_release(p_playlist);
1595 return NSDragOperationNone;
1598 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1600 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1602 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1604 if( !p_playlist ) return NO;
1606 /* Drag & Drop inside the playlist */
1607 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1611 playlist_item_t *p_new_parent, *p_item = NULL;
1612 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1614 /* If the item is to be dropped as root item of the outline, make it a
1615 child of the General node.
1616 Else, choose the proposed parent as parent. */
1618 p_new_parent = p_playlist->p_general;
1620 p_new_parent = [item pointerValue];
1622 /* If the proposed parent is not a node, then use the parent node of
1624 if( p_new_parent->i_children <= 0 )
1627 playlist_item_t *p_temp_item = p_new_parent;
1628 p_new_parent = [self parentOfItem: p_new_parent];
1631 vlc_object_release(p_playlist);
1634 /* Calculate the position of the dropped item in this new parent:
1635 following the first proposed parent. */
1636 for( j = 0; j < p_new_parent->i_children; j++ )
1638 if( p_new_parent->pp_children[j] == p_temp_item )
1643 else if( j == p_new_parent->i_children - 1 )
1648 for( i = 0; i < [o_all_items count]; i++ )
1650 playlist_item_t *p_old_parent = NULL;
1651 int i_old_index = 0;
1653 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1654 p_old_parent = [self parentOfItem: p_item];
1657 /* We may need the old index later */
1658 if( p_new_parent == p_old_parent )
1661 for( j = 0; j < p_old_parent->i_children; j++ )
1663 if( p_old_parent->pp_children[j] == p_item )
1672 /* If we move the playing item in a different node or we move the
1673 node containing the playing item in a different node, then stop
1674 playback, or the playlist refuses to detach the item. */
1675 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1676 (( p_item == p_playlist->status.p_item &&
1677 p_new_parent != p_old_parent) ||
1678 ( p_item->i_children > 0 &&
1679 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1681 playlist_Stop( p_playlist );
1683 vlc_mutex_lock( &p_playlist->object_lock );
1684 // Acually detach the item from the old position
1685 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1687 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1691 /* Calculate the new index */
1694 /* If we move the item in the same node, we need to take into
1695 account that one item will be deleted */
1696 else if((p_new_parent == p_old_parent &&
1697 i_old_index < index + (int)i)
1698 || p_new_parent == p_playlist->p_general || index == 0 )
1699 i_new_index = index + i;
1701 i_new_index = index + i + 1;
1702 // Reattach the item to the new position
1703 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1704 p_new_parent, i_new_index );
1706 vlc_mutex_unlock( &p_playlist->object_lock );
1708 [self playlistUpdated];
1709 i_row = [o_outline_view rowForItem:[o_outline_dict
1710 objectForKey:[NSString stringWithFormat: @"%p",
1711 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1715 i_row = [o_outline_view rowForItem:[o_outline_dict
1716 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1719 [o_outline_view deselectAll: self];
1720 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1721 [o_outline_view scrollRowToVisible: i_row];
1723 vlc_object_release(p_playlist);
1727 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1730 playlist_item_t *p_node = [item pointerValue];
1732 NSArray *o_array = [NSArray array];
1733 NSArray *o_values = [[o_pasteboard propertyListForType:
1734 NSFilenamesPboardType]
1735 sortedArrayUsingSelector:
1736 @selector(caseInsensitiveCompare:)];
1738 for( i = 0; i < (int)[o_values count]; i++)
1740 NSDictionary *o_dic;
1741 o_dic = [NSDictionary dictionaryWithObject:[o_values
1742 objectAtIndex:i] forKey:@"ITEM_URL"];
1743 o_array = [o_array arrayByAddingObject: o_dic];
1748 [self appendArray: o_array atPos: index enqueue: YES];
1750 else if( p_node->i_children == -1 )
1753 playlist_item_t *p_real_node = NULL;
1755 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1757 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1759 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1762 if( i_counter == p_node->i_parents )
1764 vlc_object_release(p_playlist);
1768 [self appendNodeArray: o_array inNode: p_real_node
1769 atPos: index inView: i_current_view enqueue: YES];
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];