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 /* Only reload the outlineview if the wizard window is open since this can
339 be quite long on big playlists */
340 if( [[o_outline_view window] isVisible] )
342 [o_outline_view reloadData];
348 /*****************************************************************************
349 * VLCPlaylist implementation
350 *****************************************************************************/
351 @implementation VLCPlaylist
358 o_outline_dict = [[NSMutableDictionary alloc] init];
359 o_nodes_array = [[NSMutableArray alloc] init];
360 o_items_array = [[NSMutableArray alloc] init];
370 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
372 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
377 [super awakeFromNib];
379 [o_outline_view setDoubleAction: @selector(playItem:)];
381 [o_outline_view registerForDraggedTypes:
382 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
383 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
385 /* We need to check whether _defaultTableHeaderSortImage exists, since it
386 belongs to an Apple hidden private API, and then can "disapear" at any time*/
388 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
390 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
394 o_ascendingSortingImage = nil;
397 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
399 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
403 o_descendingSortingImage = nil;
406 o_tc_sortColumn = nil;
408 for( i_index = 0; i_index < p_list->i_count; i_index++ )
411 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
413 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
415 /* create the menu entries used in the playlist menu */
416 o_lmi = [[o_mi_services submenu] addItemWithTitle:
417 [NSString stringWithUTF8String:
418 p_parser->psz_longname ? p_parser->psz_longname :
419 ( p_parser->psz_shortname ? p_parser->psz_shortname:
420 p_parser->psz_object_name)]
421 action: @selector(servicesChange:)
423 [o_lmi setTarget: self];
424 [o_lmi setRepresentedObject:
425 [NSString stringWithCString: p_parser->psz_object_name]];
426 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
427 p_parser->psz_object_name ) )
428 [o_lmi setState: NSOnState];
430 /* create the menu entries for the main menu */
431 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
432 [NSString stringWithUTF8String:
433 p_parser->psz_longname ? p_parser->psz_longname :
434 ( p_parser->psz_shortname ? p_parser->psz_shortname:
435 p_parser->psz_object_name)]
436 action: @selector(servicesChange:)
438 [o_lmi setTarget: self];
439 [o_lmi setRepresentedObject:
440 [NSString stringWithCString: p_parser->psz_object_name]];
441 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
442 p_parser->psz_object_name ) )
443 [o_lmi setState: NSOnState];
446 vlc_list_release( p_list );
447 vlc_object_release( p_playlist );
449 /* Change the simple textfield into a searchField if we can... */
451 if( MACOS_VERSION >= 10.3 )
453 NSView *o_parentview = [o_status_field superview];
454 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
455 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
456 [o_better_search_field setDelegate:self];
457 [[NSNotificationCenter defaultCenter] addObserver: self
458 selector: @selector(searchfieldChanged:)
459 name: NSControlTextDidChangeNotification
460 object: o_better_search_field];
462 [o_better_search_field setTarget:self];
463 [o_better_search_field setAction:@selector(searchItem:)];
465 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
466 [o_parentview addSubview:o_better_search_field];
467 [o_search_field setHidden:YES];
470 //[self playlistUpdated];
473 - (void)searchfieldChanged:(NSNotification *)o_notification
475 [o_search_field setStringValue:[[o_notification object] stringValue]];
482 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
483 [o_mi_play setTitle: _NS("Play")];
484 [o_mi_delete setTitle: _NS("Delete")];
485 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
486 [o_mi_selectall setTitle: _NS("Select All")];
487 [o_mi_info setTitle: _NS("Properties")];
488 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
489 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
490 [o_mi_services setTitle: _NS("Services discovery")];
491 [o_status_field setStringValue: [NSString stringWithFormat:
492 _NS("no items in playlist")]];
494 [o_random_ckb setTitle: _NS("Random")];
496 [o_search_button setTitle: _NS("Search")];
498 [o_search_field setToolTip: _NS("Search in Playlist")];
499 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
500 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
501 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
504 - (void)playlistUpdated
508 /* Clear indications of any existing column sorting*/
509 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
511 [o_outline_view setIndicatorImage:nil inTableColumn:
512 [[o_outline_view tableColumns] objectAtIndex:i]];
515 [o_outline_view setHighlightedTableColumn:nil];
516 o_tc_sortColumn = nil;
517 // TODO Find a way to keep the dict size to a minimum
518 //[o_outline_dict removeAllObjects];
519 [o_outline_view reloadData];
520 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
523 - (void)playModeUpdated
525 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
527 vlc_value_t val, val2;
529 if( p_playlist == NULL )
534 var_Get( p_playlist, "loop", &val2 );
535 var_Get( p_playlist, "repeat", &val );
536 if( val.b_bool == VLC_TRUE )
538 [o_loop_popup selectItemAtIndex: 1];
540 else if( val2.b_bool == VLC_TRUE )
542 [o_loop_popup selectItemAtIndex: 2];
546 [o_loop_popup selectItemAtIndex: 0];
549 var_Get( p_playlist, "random", &val );
550 [o_random_ckb setState: val.b_bool];
552 vlc_object_release( p_playlist );
555 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
558 for( i = 0 ; i < p_item->i_parents; i++ )
560 if( p_item->pp_parents[i]->i_view == i_current_view )
562 return p_item->pp_parents[i]->p_parent;
568 - (void)updateRowSelection
574 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
576 playlist_item_t *p_item, *p_temp_item;
577 NSMutableArray *o_array = [NSMutableArray array];
579 if( p_playlist == NULL )
582 p_item = p_playlist->status.p_item;
585 vlc_object_release(p_playlist);
589 p_temp_item = p_item;
590 while( p_temp_item->i_parents > 0 )
592 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
594 p_temp_item = [self parentOfItem: p_temp_item];
595 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
597 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
599 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
605 for (j = 0 ; j < [o_array count] - 1 ; j++)
608 if( ( o_item = [o_outline_dict objectForKey:
609 [NSString stringWithFormat: @"%p",
610 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
611 [o_outline_view expandItem: o_item];
615 i_row = [o_outline_view rowForItem:[o_outline_dict
616 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
618 [o_outline_view selectRow: i_row byExtendingSelection: NO];
619 [o_outline_view scrollRowToVisible: i_row];
621 vlc_object_release(p_playlist);
624 /* 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
625 deleted. We don't do it when not required since this verification takes
626 quite a long time on big playlists (yes, pretty hacky). */
627 - (BOOL)isItem: (playlist_item_t *)p_item
628 inNode: (playlist_item_t *)p_node
629 checkItemExistence:(BOOL)b_check
632 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
634 playlist_item_t *p_temp_item = p_item;
636 if( p_playlist == NULL )
641 if( p_node == p_item )
643 vlc_object_release(p_playlist);
647 if( p_node->i_children < 1)
649 vlc_object_release(p_playlist);
656 vlc_mutex_lock( &p_playlist->object_lock );
660 /* Since outlineView: willDisplayCell:... may call this function with
661 p_items that don't exist anymore, first check if the item is still
662 in the playlist. Any cleaner solution welcomed. */
663 for( i = 0; i < p_playlist->i_all_size; i++ )
665 if( p_playlist->pp_all_items[i] == p_item ) break;
666 else if ( i == p_playlist->i_all_size - 1 )
668 vlc_object_release( p_playlist );
669 vlc_mutex_unlock( &p_playlist->object_lock );
675 while( p_temp_item->i_parents > 0 )
677 p_temp_item = [self parentOfItem: p_temp_item];
678 if( p_temp_item == p_node )
680 vlc_mutex_unlock( &p_playlist->object_lock );
681 vlc_object_release( p_playlist );
685 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
687 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
689 if( p_temp_item->pp_parents[i]->p_parent == p_node )
691 vlc_mutex_unlock( &p_playlist->object_lock );
692 vlc_object_release( p_playlist );
697 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
703 vlc_mutex_unlock( &p_playlist->object_lock );
706 vlc_object_release( p_playlist );
710 /* This method is usefull for instance to remove the selected children of an
711 already selected node */
712 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
715 for( i = 0 ; i < [o_items count] ; i++ )
717 for ( j = 0 ; j < [o_nodes count] ; j++ )
719 if( o_items == o_nodes)
721 if( j == i ) continue;
723 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
724 inNode: [[o_nodes objectAtIndex:j] pointerValue]
725 checkItemExistence: NO] )
727 [o_items removeObjectAtIndex:i];
728 /* We need to execute the next iteration with the same index
729 since the current item has been deleted */
738 - (IBAction)savePlaylist:(id)sender
740 intf_thread_t * p_intf = VLCIntf;
741 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
744 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
745 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
746 [o_save_panel setTitle: _NS("Save Playlist")];
747 [o_save_panel setPrompt: _NS("Save")];
749 if( [o_save_panel runModalForDirectory: nil
750 file: o_name] == NSOKButton )
752 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
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;
770 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
774 if( p_item->i_children == -1 )
776 p_node = [self parentOfItem: p_item];
778 /* for( i = 0 ; i < p_item->i_parents ; i++ )
780 if( p_item->pp_parents[i]->i_view == i_current_view )
782 p_node = p_item->pp_parents[i]->p_parent;
789 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
791 p_item = p_node->pp_children[0];
798 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
800 vlc_object_release( p_playlist );
804 - (IBAction)servicesChange:(id)sender
806 NSMenuItem *o_mi = (NSMenuItem *)sender;
807 NSString *o_string = [o_mi representedObject];
808 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
810 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
811 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
813 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
815 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
816 [o_string cString] ) ? YES : NO];
818 i_current_view = VIEW_CATEGORY;
819 playlist_ViewUpdate( p_playlist, i_current_view );
820 vlc_object_release( p_playlist );
821 [self playlistUpdated];
825 - (IBAction)selectAll:(id)sender
827 [o_outline_view selectAll: nil];
830 - (IBAction)deleteItem:(id)sender
832 int i, i_count, i_row;
833 NSMutableArray *o_to_delete;
836 playlist_t * p_playlist;
837 intf_thread_t * p_intf = VLCIntf;
839 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
842 if ( p_playlist == NULL )
846 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
847 i_count = [o_to_delete count];
849 for( i = 0; i < i_count; i++ )
851 o_number = [o_to_delete lastObject];
852 i_row = [o_number intValue];
853 id o_item = [o_outline_view itemAtRow: i_row];
854 playlist_item_t *p_item = [o_item pointerValue];
855 [o_to_delete removeObject: o_number];
856 [o_outline_view deselectRow: i_row];
858 if( [[o_outline_view dataSource] outlineView:o_outline_view
859 numberOfChildrenOfItem: o_item] > 0 )
860 //is a node and not an item
862 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
863 [self isItem: p_playlist->status.p_item inNode:
864 ((playlist_item_t *)[o_item pointerValue])
865 checkItemExistence: NO] == YES )
867 // if current item is in selected node and is playing then stop playlist
868 playlist_Stop( p_playlist );
870 vlc_mutex_lock( &p_playlist->object_lock );
871 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
872 vlc_mutex_unlock( &p_playlist->object_lock );
876 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
877 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
879 playlist_Stop( p_playlist );
881 vlc_mutex_lock( &p_playlist->object_lock );
882 playlist_Delete( p_playlist, p_item->input.i_id );
883 vlc_mutex_unlock( &p_playlist->object_lock );
886 [self playlistUpdated];
887 vlc_object_release( p_playlist );
890 - (IBAction)sortNodeByName:(id)sender
892 [self sortNode: SORT_TITLE];
895 - (IBAction)sortNodeByAuthor:(id)sender
897 [self sortNode: SORT_AUTHOR];
900 - (void)sortNode:(int)i_mode
902 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
904 playlist_item_t * p_item;
906 if (p_playlist == NULL)
911 if( [o_outline_view selectedRow] > -1 )
913 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
917 /*If no item is selected, sort the whole playlist*/
919 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
920 p_item = p_view->p_root;
923 if( p_item->i_children > -1 ) // the item is a node
925 vlc_mutex_lock( &p_playlist->object_lock );
926 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
927 vlc_mutex_unlock( &p_playlist->object_lock );
933 for( i = 0 ; i < p_item->i_parents ; i++ )
935 if( p_item->pp_parents[i]->i_view == i_current_view )
937 vlc_mutex_lock( &p_playlist->object_lock );
938 playlist_RecursiveNodeSort( p_playlist,
939 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
940 vlc_mutex_unlock( &p_playlist->object_lock );
945 vlc_object_release( p_playlist );
946 [self playlistUpdated];
949 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
951 intf_thread_t * p_intf = VLCIntf;
952 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
955 if( p_playlist == NULL )
959 playlist_item_t *p_item;
961 BOOL b_rem = FALSE, b_dir = FALSE;
962 NSString *o_uri, *o_name;
967 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
968 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
969 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
971 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
972 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
974 int i_count, i_index;
975 struct statfs *mounts = NULL;
977 i_count = getmntinfo (&mounts, MNT_NOWAIT);
978 /* getmntinfo returns a pointer to static data. Do not free. */
979 for( i_index = 0 ; i_index < i_count; i_index++ )
981 NSMutableString *o_temp, *o_temp2;
982 o_temp = [NSMutableString stringWithString: o_uri];
983 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
984 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
985 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
986 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
988 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
990 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
994 /* If no name, then make a guess */
995 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
997 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
998 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
999 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1001 /* All of this is to make sure CD's play when you D&D them on VLC */
1002 /* Converts mountpoint to a /dev file */
1005 NSMutableString *o_temp;
1007 buf = (struct statfs *) malloc (sizeof(struct statfs));
1008 statfs( [o_uri fileSystemRepresentation], buf );
1009 psz_dev = strdup(buf->f_mntfromname);
1010 o_temp = [NSMutableString stringWithCString: psz_dev ];
1011 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1012 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1013 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1017 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1023 for( i = 0; i < (int)[o_options count]; i++ )
1025 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1029 /* Recent documents menu */
1030 o_true_file = [NSURL fileURLWithPath: o_uri];
1031 if( o_true_file != nil )
1033 [[NSDocumentController sharedDocumentController]
1034 noteNewRecentDocumentURL: o_true_file];
1037 vlc_object_release( p_playlist );
1041 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1044 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1046 if( p_playlist == NULL )
1051 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1053 playlist_item_t *p_item;
1054 NSDictionary *o_one_item;
1057 o_one_item = [o_array objectAtIndex: i_item];
1058 p_item = [self createItem: o_one_item];
1065 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1067 if( i_item == 0 && !b_enqueue )
1069 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1072 vlc_object_release( p_playlist );
1075 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1078 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1080 if( p_playlist == NULL )
1085 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1087 playlist_item_t *p_item;
1088 NSDictionary *o_one_item;
1091 o_one_item = [o_array objectAtIndex: i_item];
1092 p_item = [self createItem: o_one_item];
1099 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1101 if( i_item == 0 && !b_enqueue )
1103 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1106 vlc_object_release( p_playlist );
1110 - (IBAction)handlePopUp:(id)sender
1113 intf_thread_t * p_intf = VLCIntf;
1114 vlc_value_t val1,val2;
1115 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1117 if( p_playlist == NULL )
1122 switch( [o_loop_popup indexOfSelectedItem] )
1127 var_Set( p_playlist, "loop", val1 );
1129 var_Set( p_playlist, "repeat", val1 );
1130 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1135 var_Set( p_playlist, "repeat", val1 );
1137 var_Set( p_playlist, "loop", val1 );
1138 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1142 var_Get( p_playlist, "repeat", &val1 );
1143 var_Get( p_playlist, "loop", &val2 );
1144 if( val1.b_bool || val2.b_bool )
1147 var_Set( p_playlist, "repeat", val1 );
1148 var_Set( p_playlist, "loop", val1 );
1149 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1153 vlc_object_release( p_playlist );
1154 [self playlistUpdated];
1157 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1159 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1161 playlist_item_t *p_selected_item;
1162 int i_current, i_selected_row;
1167 i_selected_row = [o_outline_view selectedRow];
1168 if (i_selected_row < 0)
1171 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1172 i_selected_row] pointerValue];
1174 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1177 NSString *o_current_name, *o_current_author;
1179 vlc_mutex_lock( &p_playlist->object_lock );
1180 o_current_name = [NSString stringWithUTF8String:
1181 p_item->pp_children[i_current]->input.psz_name];
1182 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1183 _("Meta-information"),_("Artist") );
1184 o_current_author = [NSString stringWithUTF8String: psz_temp];
1186 vlc_mutex_unlock( &p_playlist->object_lock );
1188 if( p_selected_item == p_item->pp_children[i_current] &&
1189 b_selected_item_met == NO )
1191 b_selected_item_met = YES;
1193 else if( p_selected_item == p_item->pp_children[i_current] &&
1194 b_selected_item_met == YES )
1196 vlc_object_release( p_playlist );
1199 else if( b_selected_item_met == YES &&
1200 ( [o_current_name rangeOfString:[o_search_field
1201 stringValue] options:NSCaseInsensitiveSearch ].length ||
1202 [o_current_author rangeOfString:[o_search_field
1203 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1205 vlc_object_release( p_playlist );
1206 /*Adds the parent items in the result array as well, so that we can
1208 return [NSMutableArray arrayWithObject: [NSValue
1209 valueWithPointer: p_item->pp_children[i_current]]];
1211 if( p_item->pp_children[i_current]->i_children > 0 )
1213 id o_result = [self subSearchItem:
1214 p_item->pp_children[i_current]];
1215 if( o_result != NULL )
1217 vlc_object_release( p_playlist );
1218 [o_result insertObject: [NSValue valueWithPointer:
1219 p_item->pp_children[i_current]] atIndex:0];
1224 vlc_object_release( p_playlist );
1228 - (IBAction)searchItem:(id)sender
1230 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1232 playlist_view_t * p_view;
1238 b_selected_item_met = NO;
1240 if( p_playlist == NULL )
1242 p_view = playlist_ViewFind( p_playlist, i_current_view );
1246 /*First, only search after the selected item:*
1247 *(b_selected_item_met = NO) */
1248 o_result = [self subSearchItem:p_view->p_root];
1249 if( o_result == NULL )
1251 /* If the first search failed, search again from the beginning */
1252 o_result = [self subSearchItem:p_view->p_root];
1254 if( o_result != NULL )
1257 if( [[o_result objectAtIndex: 0] pointerValue] ==
1258 p_playlist->p_general )
1263 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1265 [o_outline_view expandItem: [o_outline_dict objectForKey:
1266 [NSString stringWithFormat: @"%p",
1267 [[o_result objectAtIndex: i] pointerValue]]]];
1269 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1270 [NSString stringWithFormat: @"%p",
1271 [[o_result objectAtIndex: [o_result count] - 1 ]
1276 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1277 [o_outline_view scrollRowToVisible: i_row];
1280 vlc_object_release( p_playlist );
1283 - (IBAction)recursiveExpandNode:(id)sender
1286 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1287 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1289 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1290 isItemExpandable: o_item] )
1292 for( i = 0 ; i < p_item->i_parents ; i++ )
1294 if( p_item->pp_parents[i]->i_view == i_current_view )
1296 o_item = [o_outline_dict objectForKey: [NSString
1297 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1303 /* We need to collapse the node first, since OSX refuses to recursively
1304 expand an already expanded node, even if children nodes are collapsed. */
1305 [o_outline_view collapseItem: o_item collapseChildren: YES];
1306 [o_outline_view expandItem: o_item expandChildren: YES];
1309 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1313 vlc_bool_t b_item_sel;
1315 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1317 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1318 [o_outline_view selectedRow] != -1 );
1319 b_rows = [o_outline_view numberOfRows] != 0;
1321 [o_mi_play setEnabled: b_item_sel];
1322 [o_mi_delete setEnabled: b_item_sel];
1323 [o_mi_selectall setEnabled: b_rows];
1324 [o_mi_info setEnabled: b_item_sel];
1325 [o_mi_recursive_expand setEnabled: b_item_sel];
1326 [o_mi_sort_name setEnabled: b_item_sel];
1327 [o_mi_sort_author setEnabled: b_item_sel];
1329 return( o_ctx_menu );
1332 - (void)outlineView: (NSTableView*)o_tv
1333 didClickTableColumn:(NSTableColumn *)o_tc
1335 int i_mode = 0, i_type;
1336 intf_thread_t *p_intf = VLCIntf;
1337 playlist_view_t *p_view;
1339 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1341 if( p_playlist == NULL )
1346 /* Check whether the selected table column header corresponds to a
1347 sortable table column*/
1348 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1350 vlc_object_release( p_playlist );
1354 p_view = playlist_ViewFind( p_playlist, i_current_view );
1356 if( o_tc_sortColumn == o_tc )
1358 b_isSortDescending = !b_isSortDescending;
1362 b_isSortDescending = VLC_FALSE;
1365 if( o_tc == o_tc_name )
1367 i_mode = SORT_TITLE;
1369 else if( o_tc == o_tc_author )
1371 i_mode = SORT_AUTHOR;
1374 if( b_isSortDescending )
1376 i_type = ORDER_REVERSE;
1380 i_type = ORDER_NORMAL;
1383 vlc_mutex_lock( &p_playlist->object_lock );
1384 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1385 vlc_mutex_unlock( &p_playlist->object_lock );
1387 vlc_object_release( p_playlist );
1388 [self playlistUpdated];
1390 o_tc_sortColumn = o_tc;
1391 [o_outline_view setHighlightedTableColumn:o_tc];
1393 if( b_isSortDescending )
1395 [o_outline_view setIndicatorImage:o_descendingSortingImage
1396 inTableColumn:o_tc];
1400 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1401 inTableColumn:o_tc];
1406 - (void)outlineView:(NSOutlineView *)outlineView
1407 willDisplayCell:(id)cell
1408 forTableColumn:(NSTableColumn *)tableColumn
1411 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1416 if( !p_playlist ) return;
1418 o_playing_item = [o_outline_dict objectForKey:
1419 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1421 if( [self isItem: [o_playing_item pointerValue] inNode:
1422 [item pointerValue] checkItemExistence: YES]
1423 || [o_playing_item isEqual: item] )
1425 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1429 [cell setFont: [NSFont systemFontOfSize: 0]];
1431 vlc_object_release( p_playlist );
1436 @implementation VLCPlaylist (NSOutlineViewDataSource)
1438 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1440 id o_value = [super outlineView: outlineView child: index ofItem: item];
1441 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1444 if( !p_playlist ) return nil;
1446 if( p_playlist->i_size >= 2 )
1448 [o_status_field setStringValue: [NSString stringWithFormat:
1449 _NS("%i items in playlist"), p_playlist->i_size]];
1453 if( p_playlist->i_size == 0 )
1455 [o_status_field setStringValue: [NSString stringWithFormat:
1456 _NS("no items in playlist"), p_playlist->i_size]];
1460 [o_status_field setStringValue: [NSString stringWithFormat:
1461 _NS("1 item in playlist"), p_playlist->i_size]];
1464 vlc_object_release( p_playlist );
1466 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1467 [o_value pointerValue]]];
1473 /* Required for drag & drop and reordering */
1474 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1477 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1480 /* First remove the items that were moved during the last drag & drop
1482 [o_items_array removeAllObjects];
1483 [o_nodes_array removeAllObjects];
1485 if( !p_playlist ) return NO;
1487 for( i = 0 ; i < [items count] ; i++ )
1489 id o_item = [items objectAtIndex: i];
1491 /* Refuse to move items that are not in the General Node
1492 (Service Discovery) */
1493 if( ![self isItem: [o_item pointerValue] inNode:
1494 p_playlist->p_general checkItemExistence: NO])
1496 vlc_object_release(p_playlist);
1499 /* Fill the items and nodes to move in 2 different arrays */
1500 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1501 [o_nodes_array addObject: o_item];
1503 [o_items_array addObject: o_item];
1506 /* Now we need to check if there are selected items that are in already
1507 selected nodes. In that case, we only want to move the nodes */
1508 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1509 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1512 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1514 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1516 if( j == i ) continue;
1517 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1518 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1520 [o_nodes_array removeObjectAtIndex:i];
1521 /* We need to execute the next iteration with the same index
1522 since the current item has been deleted */
1529 for( i = 0 ; i < [o_items_array count] ; i++ )
1531 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1533 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1534 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1536 [o_items_array removeObjectAtIndex:i];
1543 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1544 a Drop operation comçing from the playlist.
1545 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1548 [pboard declareTypes: [NSArray arrayWithObjects:
1549 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1550 [pboard setPropertyList:[NSArray array]
1551 forType:NSFilenamesPboardType];
1553 vlc_object_release(p_playlist);
1557 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1559 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1561 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1563 if( !p_playlist ) return NSDragOperationNone;
1565 /* We refuse to drop an item in anything else than a child of the General
1566 Node. We still accept items that would be root nodes of the outlineview
1567 however, to allow drop in an empty playlist.*/
1568 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1569 checkItemExistence: NO] || item == nil) )
1571 vlc_object_release(p_playlist);
1572 return NSDragOperationNone;
1575 /* Drop from the Playlist */
1576 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1579 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1581 /* We refuse to Drop in a child of an item we are moving */
1582 if( [self isItem: [item pointerValue] inNode:
1583 [[o_nodes_array objectAtIndex: i] pointerValue]
1584 checkItemExistence: NO] )
1586 vlc_object_release(p_playlist);
1587 return NSDragOperationNone;
1590 vlc_object_release(p_playlist);
1591 return NSDragOperationMove;
1594 /* Drop from the Finder */
1595 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1597 vlc_object_release(p_playlist);
1598 return NSDragOperationGeneric;
1600 vlc_object_release(p_playlist);
1601 return NSDragOperationNone;
1604 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1606 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1608 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1610 if( !p_playlist ) return NO;
1612 /* Drag & Drop inside the playlist */
1613 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1617 playlist_item_t *p_new_parent, *p_item = NULL;
1618 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1620 /* If the item is to be dropped as root item of the outline, make it a
1621 child of the General node.
1622 Else, choose the proposed parent as parent. */
1624 p_new_parent = p_playlist->p_general;
1626 p_new_parent = [item pointerValue];
1628 /* If the proposed parent is not a node, then use the parent node of
1630 if( p_new_parent->i_children <= 0 )
1633 playlist_item_t *p_temp_item = p_new_parent;
1634 p_new_parent = [self parentOfItem: p_new_parent];
1637 vlc_object_release(p_playlist);
1640 /* Calculate the position of the dropped item in this new parent:
1641 following the first proposed parent. */
1642 for( j = 0; j < p_new_parent->i_children; j++ )
1644 if( p_new_parent->pp_children[j] == p_temp_item )
1649 else if( j == p_new_parent->i_children - 1 )
1654 for( i = 0; i < [o_all_items count]; i++ )
1656 playlist_item_t *p_old_parent = NULL;
1657 int i_old_index = 0;
1659 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1660 p_old_parent = [self parentOfItem: p_item];
1663 /* We may need the old index later */
1664 if( p_new_parent == p_old_parent )
1667 for( j = 0; j < p_old_parent->i_children; j++ )
1669 if( p_old_parent->pp_children[j] == p_item )
1678 /* If we move the playing item in a different node or we move the
1679 node containing the playing item in a different node, then stop
1680 playback, or the playlist refuses to detach the item. */
1681 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1682 (( p_item == p_playlist->status.p_item &&
1683 p_new_parent != p_old_parent) ||
1684 ( p_item->i_children > 0 &&
1685 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1687 playlist_Stop( p_playlist );
1689 vlc_mutex_lock( &p_playlist->object_lock );
1690 // Acually detach the item from the old position
1691 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1693 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1697 /* Calculate the new index */
1700 /* If we move the item in the same node, we need to take into
1701 account that one item will be deleted */
1702 else if((p_new_parent == p_old_parent &&
1703 i_old_index < index + (int)i)
1704 || p_new_parent == p_playlist->p_general || index == 0 )
1705 i_new_index = index + i;
1707 i_new_index = index + i + 1;
1708 // Reattach the item to the new position
1709 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1710 p_new_parent, i_new_index );
1712 vlc_mutex_unlock( &p_playlist->object_lock );
1714 [self playlistUpdated];
1715 i_row = [o_outline_view rowForItem:[o_outline_dict
1716 objectForKey:[NSString stringWithFormat: @"%p",
1717 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1721 i_row = [o_outline_view rowForItem:[o_outline_dict
1722 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1725 [o_outline_view deselectAll: self];
1726 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1727 [o_outline_view scrollRowToVisible: i_row];
1729 vlc_object_release(p_playlist);
1733 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1736 playlist_item_t *p_node = [item pointerValue];
1738 NSArray *o_array = [NSArray array];
1739 NSArray *o_values = [[o_pasteboard propertyListForType:
1740 NSFilenamesPboardType]
1741 sortedArrayUsingSelector:
1742 @selector(caseInsensitiveCompare:)];
1744 for( i = 0; i < (int)[o_values count]; i++)
1746 NSDictionary *o_dic;
1747 o_dic = [NSDictionary dictionaryWithObject:[o_values
1748 objectAtIndex:i] forKey:@"ITEM_URL"];
1749 o_array = [o_array arrayByAddingObject: o_dic];
1754 [self appendArray: o_array atPos: index enqueue: YES];
1756 else if( p_node->i_children == -1 )
1759 playlist_item_t *p_real_node = NULL;
1761 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1763 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1765 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1768 if( i_counter == p_node->i_parents )
1770 vlc_object_release(p_playlist);
1774 [self appendNodeArray: o_array inNode: p_real_node
1775 atPos: index inView: i_current_view enqueue: YES];
1779 [self appendNodeArray: o_array inNode: p_node
1780 atPos: index inView: i_current_view enqueue: YES];
1782 vlc_object_release( p_playlist );
1785 vlc_object_release( p_playlist );
1789 /* Delegate method of NSWindow */
1790 /*- (void)windowWillClose:(NSNotification *)aNotification
1792 [o_btn_playlist setState: NSOffState];