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>
54 /*****************************************************************************
55 * VLCPlaylistView implementation
56 *****************************************************************************/
57 @implementation VLCPlaylistView
59 - (NSMenu *)menuForEvent:(NSEvent *)o_event
61 return( [[self delegate] menuForEvent: o_event] );
64 - (void)keyDown:(NSEvent *)o_event
68 if( [[o_event characters] length] )
70 key = [[o_event characters] characterAtIndex: 0];
75 case NSDeleteCharacter:
76 case NSDeleteFunctionKey:
77 case NSDeleteCharFunctionKey:
78 case NSBackspaceCharacter:
79 [[self delegate] deleteItem:self];
82 case NSEnterCharacter:
83 case NSCarriageReturnCharacter:
84 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
89 [super keyDown: o_event];
97 /*****************************************************************************
98 * VLCPlaylistCommon implementation
100 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
101 * It contains the common methods and elements of these 2 entities.
102 *****************************************************************************/
103 @implementation VLCPlaylistCommon
107 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
109 i_current_view = VIEW_CATEGORY;
110 playlist_ViewUpdate( p_playlist, i_current_view );
112 [o_outline_view setTarget: self];
113 [o_outline_view setDelegate: self];
114 [o_outline_view setDataSource: self];
116 vlc_object_release( p_playlist );
122 [[o_tc_name headerCell] setStringValue:_NS("Name")];
123 [[o_tc_author headerCell] setStringValue:_NS("Author")];
124 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
127 - (NSOutlineView *)outlineView
129 return o_outline_view;
132 - (playlist_item_t *)selectedPlaylistItem
134 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
140 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
142 /* return the number of children for Obj-C pointer item */ /* DONE */
143 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
146 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
148 if( p_playlist == NULL )
150 if( outlineView != o_outline_view )
152 vlc_object_release( p_playlist );
159 playlist_view_t *p_view;
160 p_view = playlist_ViewFind( p_playlist, i_current_view );
161 if( p_view && p_view->p_root )
163 i_return = p_view->p_root->i_children;
164 if( i_current_view == VIEW_CATEGORY )
166 i_return--; /* remove the GENERAL item from the list */
167 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
173 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
175 i_return = p_item->i_children;
177 vlc_object_release( p_playlist );
185 /* return the child at index for the Obj-C pointer item */ /* DONE */
186 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
188 playlist_item_t *p_return = NULL;
189 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
193 if( p_playlist == NULL )
199 playlist_view_t *p_view;
200 p_view = playlist_ViewFind( p_playlist, i_current_view );
201 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
203 if( i_current_view == VIEW_CATEGORY )
205 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
207 p_return = p_playlist->p_general->pp_children[index];
209 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
211 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
217 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
218 if( p_item && index < p_item->i_children && index >= 0 )
219 p_return = p_item->pp_children[index];
223 vlc_object_release( p_playlist );
225 o_value = [[NSValue valueWithPointer: p_return] retain];
230 /* is the item expandable */
231 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
234 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
236 if( p_playlist == NULL )
242 playlist_view_t *p_view;
243 p_view = playlist_ViewFind( p_playlist, i_current_view );
244 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
246 if( i_current_view == VIEW_CATEGORY )
249 i_return += p_playlist->p_general->i_children;
254 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
256 i_return = p_item->i_children;
258 vlc_object_release( p_playlist );
266 /* retrieve the string values for the cells */
267 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
270 intf_thread_t *p_intf = VLCIntf;
271 playlist_t *p_playlist;
272 playlist_item_t *p_item;
274 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
276 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
278 if( p_playlist == NULL )
283 p_item = (playlist_item_t *)[item pointerValue];
287 vlc_object_release( p_playlist );
291 if( [[o_tc identifier] isEqualToString:@"1"] )
293 o_value = [NSString stringWithUTF8String:
294 p_item->input.psz_name];
295 if( o_value == NULL )
296 o_value = [NSString stringWithCString:
297 p_item->input.psz_name];
299 else if( [[o_tc identifier] isEqualToString:@"2"] )
302 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
304 if( psz_temp == NULL )
308 o_value = [NSString stringWithUTF8String: psz_temp];
309 if( o_value == NULL )
311 o_value = [NSString stringWithCString: psz_temp];
316 else if( [[o_tc identifier] isEqualToString:@"3"] )
318 char psz_duration[MSTRTIME_MAX_SIZE];
319 mtime_t dur = p_item->input.i_duration;
322 secstotimestr( psz_duration, dur/1000000 );
323 o_value = [NSString stringWithUTF8String: psz_duration];
327 o_value = @"-:--:--";
330 vlc_object_release( p_playlist );
337 /*****************************************************************************
338 * VLCPlaylistWizard implementation
339 *****************************************************************************/
340 @implementation VLCPlaylistWizard
342 - (IBAction)reloadOutlineView
344 /* Only reload the outlineview if the wizard window is open since this can
345 be quite long on big playlists */
346 if( [[o_outline_view window] isVisible] )
348 [o_outline_view reloadData];
354 /*****************************************************************************
355 * VLCPlaylist implementation
356 *****************************************************************************/
357 @implementation VLCPlaylist
364 o_outline_dict = [[NSMutableDictionary alloc] init];
365 o_nodes_array = [[NSMutableArray alloc] init];
366 o_items_array = [[NSMutableArray alloc] init];
376 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
378 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
383 [super awakeFromNib];
385 [o_outline_view setDoubleAction: @selector(playItem:)];
387 [o_outline_view registerForDraggedTypes:
388 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
389 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
391 /* We need to check whether _defaultTableHeaderSortImage exists, since it
392 belongs to an Apple hidden private API, and then can "disapear" at any time*/
394 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
396 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
400 o_ascendingSortingImage = nil;
403 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
405 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
409 o_descendingSortingImage = nil;
412 o_tc_sortColumn = nil;
414 for( i_index = 0; i_index < p_list->i_count; i_index++ )
417 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
419 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
421 /* create the menu entries used in the playlist menu */
422 o_lmi = [[o_mi_services submenu] addItemWithTitle:
423 [NSString stringWithUTF8String:
424 p_parser->psz_longname ? p_parser->psz_longname :
425 ( p_parser->psz_shortname ? p_parser->psz_shortname:
426 p_parser->psz_object_name)]
427 action: @selector(servicesChange:)
429 [o_lmi setTarget: self];
430 [o_lmi setRepresentedObject:
431 [NSString stringWithCString: p_parser->psz_object_name]];
432 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
433 p_parser->psz_object_name ) )
434 [o_lmi setState: NSOnState];
436 /* create the menu entries for the main menu */
437 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
438 [NSString stringWithUTF8String:
439 p_parser->psz_longname ? p_parser->psz_longname :
440 ( p_parser->psz_shortname ? p_parser->psz_shortname:
441 p_parser->psz_object_name)]
442 action: @selector(servicesChange:)
444 [o_lmi setTarget: self];
445 [o_lmi setRepresentedObject:
446 [NSString stringWithCString: p_parser->psz_object_name]];
447 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
448 p_parser->psz_object_name ) )
449 [o_lmi setState: NSOnState];
452 vlc_list_release( p_list );
453 vlc_object_release( p_playlist );
455 /* Change the simple textfield into a searchField if we can... */
457 if( MACOS_VERSION >= 10.3 )
459 NSView *o_parentview = [o_status_field superview];
460 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
461 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
462 [o_better_search_field setDelegate:self];
463 [[NSNotificationCenter defaultCenter] addObserver: self
464 selector: @selector(searchfieldChanged:)
465 name: NSControlTextDidChangeNotification
466 object: o_better_search_field];
468 [o_better_search_field setTarget:self];
469 [o_better_search_field setAction:@selector(searchItem:)];
471 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
472 [o_parentview addSubview:o_better_search_field];
473 [o_search_field setHidden:YES];
476 //[self playlistUpdated];
479 - (void)searchfieldChanged:(NSNotification *)o_notification
481 [o_search_field setStringValue:[[o_notification object] stringValue]];
488 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
489 [o_mi_play setTitle: _NS("Play")];
490 [o_mi_delete setTitle: _NS("Delete")];
491 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
492 [o_mi_selectall setTitle: _NS("Select All")];
493 [o_mi_info setTitle: _NS("Properties")];
494 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
495 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
496 [o_mi_services setTitle: _NS("Services discovery")];
497 [o_status_field setStringValue: [NSString stringWithFormat:
498 _NS("no items in playlist")]];
500 [o_random_ckb setTitle: _NS("Random")];
502 [o_search_button setTitle: _NS("Search")];
504 [o_search_field setToolTip: _NS("Search in Playlist")];
505 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
506 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
507 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
510 - (void)playlistUpdated
514 /* Clear indications of any existing column sorting*/
515 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
517 [o_outline_view setIndicatorImage:nil inTableColumn:
518 [[o_outline_view tableColumns] objectAtIndex:i]];
521 [o_outline_view setHighlightedTableColumn:nil];
522 o_tc_sortColumn = nil;
523 // TODO Find a way to keep the dict size to a minimum
524 //[o_outline_dict removeAllObjects];
525 [o_outline_view reloadData];
526 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
529 - (void)playModeUpdated
531 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
533 vlc_value_t val, val2;
535 if( p_playlist == NULL )
540 var_Get( p_playlist, "loop", &val2 );
541 var_Get( p_playlist, "repeat", &val );
542 if( val.b_bool == VLC_TRUE )
544 [o_loop_popup selectItemAtIndex: 1];
546 else if( val2.b_bool == VLC_TRUE )
548 [o_loop_popup selectItemAtIndex: 2];
552 [o_loop_popup selectItemAtIndex: 0];
555 var_Get( p_playlist, "random", &val );
556 [o_random_ckb setState: val.b_bool];
558 vlc_object_release( p_playlist );
561 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
564 for( i = 0 ; i < p_item->i_parents; i++ )
566 if( p_item->pp_parents[i]->i_view == i_current_view )
568 return p_item->pp_parents[i]->p_parent;
574 - (void)updateRowSelection
580 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
582 playlist_item_t *p_item, *p_temp_item;
583 NSMutableArray *o_array = [NSMutableArray array];
585 if( p_playlist == NULL )
588 p_item = p_playlist->status.p_item;
591 vlc_object_release(p_playlist);
595 p_temp_item = p_item;
596 while( p_temp_item->i_parents > 0 )
598 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
600 p_temp_item = [self parentOfItem: p_temp_item];
601 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
603 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
605 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
611 for (j = 0 ; j < [o_array count] - 1 ; j++)
614 if( ( o_item = [o_outline_dict objectForKey:
615 [NSString stringWithFormat: @"%p",
616 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
617 [o_outline_view expandItem: o_item];
621 i_row = [o_outline_view rowForItem:[o_outline_dict
622 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
624 [o_outline_view selectRow: i_row byExtendingSelection: NO];
625 [o_outline_view scrollRowToVisible: i_row];
627 vlc_object_release(p_playlist);
630 /* 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
631 deleted. We don't do it when not required since this verification takes
632 quite a long time on big playlists (yes, pretty hacky). */
633 - (BOOL)isItem: (playlist_item_t *)p_item
634 inNode: (playlist_item_t *)p_node
635 checkItemExistence:(BOOL)b_check
638 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
640 playlist_item_t *p_temp_item = p_item;
642 if( p_playlist == NULL )
647 if( p_node == p_item )
649 vlc_object_release(p_playlist);
653 if( p_node->i_children < 1)
655 vlc_object_release(p_playlist);
662 vlc_mutex_lock( &p_playlist->object_lock );
666 /* Since outlineView: willDisplayCell:... may call this function with
667 p_items that don't exist anymore, first check if the item is still
668 in the playlist. Any cleaner solution welcomed. */
669 for( i = 0; i < p_playlist->i_all_size; i++ )
671 if( p_playlist->pp_all_items[i] == p_item ) break;
672 else if ( i == p_playlist->i_all_size - 1 )
674 vlc_object_release( p_playlist );
675 vlc_mutex_unlock( &p_playlist->object_lock );
681 while( p_temp_item->i_parents > 0 )
683 p_temp_item = [self parentOfItem: p_temp_item];
684 if( p_temp_item == p_node )
686 vlc_mutex_unlock( &p_playlist->object_lock );
687 vlc_object_release( p_playlist );
691 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
693 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
695 if( p_temp_item->pp_parents[i]->p_parent == p_node )
697 vlc_mutex_unlock( &p_playlist->object_lock );
698 vlc_object_release( p_playlist );
703 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
709 vlc_mutex_unlock( &p_playlist->object_lock );
712 vlc_object_release( p_playlist );
716 /* This method is usefull for instance to remove the selected children of an
717 already selected node */
718 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
721 for( i = 0 ; i < [o_items count] ; i++ )
723 for ( j = 0 ; j < [o_nodes count] ; j++ )
725 if( o_items == o_nodes)
727 if( j == i ) continue;
729 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
730 inNode: [[o_nodes objectAtIndex:j] pointerValue]
731 checkItemExistence: NO] )
733 [o_items removeObjectAtIndex:i];
734 /* We need to execute the next iteration with the same index
735 since the current item has been deleted */
744 - (IBAction)savePlaylist:(id)sender
746 intf_thread_t * p_intf = VLCIntf;
747 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
750 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
751 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
752 [o_save_panel setTitle: _NS("Save Playlist")];
753 [o_save_panel setPrompt: _NS("Save")];
755 if( [o_save_panel runModalForDirectory: nil
756 file: o_name] == NSOKButton )
758 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
760 vlc_object_release( p_playlist );
764 /* When called retrieves the selected outlineview row and plays that node or item */
765 - (IBAction)playItem:(id)sender
767 intf_thread_t * p_intf = VLCIntf;
768 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
771 if( p_playlist != NULL )
773 playlist_item_t *p_item;
774 playlist_item_t *p_node = NULL;
777 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
781 if( p_item->i_children == -1 )
783 p_node = [self parentOfItem: p_item];
785 /* for( i = 0 ; i < p_item->i_parents ; i++ )
787 if( p_item->pp_parents[i]->i_view == i_current_view )
789 p_node = p_item->pp_parents[i]->p_parent;
796 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
798 p_item = p_node->pp_children[0];
805 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
807 vlc_object_release( p_playlist );
811 - (IBAction)servicesChange:(id)sender
813 NSMenuItem *o_mi = (NSMenuItem *)sender;
814 NSString *o_string = [o_mi representedObject];
815 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
817 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
818 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
820 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
822 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
823 [o_string cString] ) ? YES : NO];
825 i_current_view = VIEW_CATEGORY;
826 playlist_ViewUpdate( p_playlist, i_current_view );
827 vlc_object_release( p_playlist );
828 [self playlistUpdated];
832 - (IBAction)selectAll:(id)sender
834 [o_outline_view selectAll: nil];
837 - (IBAction)deleteItem:(id)sender
839 int i, i_count, i_row;
840 NSMutableArray *o_to_delete;
843 playlist_t * p_playlist;
844 intf_thread_t * p_intf = VLCIntf;
846 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
849 if ( p_playlist == NULL )
853 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
854 i_count = [o_to_delete count];
856 for( i = 0; i < i_count; i++ )
858 o_number = [o_to_delete lastObject];
859 i_row = [o_number intValue];
860 id o_item = [o_outline_view itemAtRow: i_row];
861 playlist_item_t *p_item = [o_item pointerValue];
862 [o_to_delete removeObject: o_number];
863 [o_outline_view deselectRow: i_row];
865 if( [[o_outline_view dataSource] outlineView:o_outline_view
866 numberOfChildrenOfItem: o_item] > 0 )
867 //is a node and not an item
869 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
870 [self isItem: p_playlist->status.p_item inNode:
871 ((playlist_item_t *)[o_item pointerValue])
872 checkItemExistence: NO] == YES )
874 // if current item is in selected node and is playing then stop playlist
875 playlist_Stop( p_playlist );
877 vlc_mutex_lock( &p_playlist->object_lock );
878 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
879 vlc_mutex_unlock( &p_playlist->object_lock );
883 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
884 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
886 playlist_Stop( p_playlist );
888 vlc_mutex_lock( &p_playlist->object_lock );
889 playlist_Delete( p_playlist, p_item->input.i_id );
890 vlc_mutex_unlock( &p_playlist->object_lock );
893 [self playlistUpdated];
894 vlc_object_release( p_playlist );
897 - (IBAction)sortNodeByName:(id)sender
899 [self sortNode: SORT_TITLE];
902 - (IBAction)sortNodeByAuthor:(id)sender
904 [self sortNode: SORT_AUTHOR];
907 - (void)sortNode:(int)i_mode
909 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
911 playlist_item_t * p_item;
913 if (p_playlist == NULL)
918 if( [o_outline_view selectedRow] > -1 )
920 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
924 /*If no item is selected, sort the whole playlist*/
926 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
927 p_item = p_view->p_root;
930 if( p_item->i_children > -1 ) // the item is a node
932 vlc_mutex_lock( &p_playlist->object_lock );
933 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
934 vlc_mutex_unlock( &p_playlist->object_lock );
940 for( i = 0 ; i < p_item->i_parents ; i++ )
942 if( p_item->pp_parents[i]->i_view == i_current_view )
944 vlc_mutex_lock( &p_playlist->object_lock );
945 playlist_RecursiveNodeSort( p_playlist,
946 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
947 vlc_mutex_unlock( &p_playlist->object_lock );
952 vlc_object_release( p_playlist );
953 [self playlistUpdated];
956 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
958 intf_thread_t * p_intf = VLCIntf;
959 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
962 if( p_playlist == NULL )
966 playlist_item_t *p_item;
968 BOOL b_rem = FALSE, b_dir = FALSE;
969 NSString *o_uri, *o_name;
974 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
975 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
976 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
978 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
979 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
981 int i_count, i_index;
982 struct statfs *mounts = NULL;
984 i_count = getmntinfo (&mounts, MNT_NOWAIT);
985 /* getmntinfo returns a pointer to static data. Do not free. */
986 for( i_index = 0 ; i_index < i_count; i_index++ )
988 NSMutableString *o_temp, *o_temp2;
989 o_temp = [NSMutableString stringWithString: o_uri];
990 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
991 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
992 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
993 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
995 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
997 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1001 /* If no name, then make a guess */
1002 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1004 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1005 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1006 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1008 /* All of this is to make sure CD's play when you D&D them on VLC */
1009 /* Converts mountpoint to a /dev file */
1012 NSMutableString *o_temp;
1014 buf = (struct statfs *) malloc (sizeof(struct statfs));
1015 statfs( [o_uri fileSystemRepresentation], buf );
1016 psz_dev = strdup(buf->f_mntfromname);
1017 o_temp = [NSMutableString stringWithCString: psz_dev ];
1018 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1019 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1020 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1024 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1030 for( i = 0; i < (int)[o_options count]; i++ )
1032 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1036 /* Recent documents menu */
1037 o_true_file = [NSURL fileURLWithPath: o_uri];
1038 if( o_true_file != nil )
1040 [[NSDocumentController sharedDocumentController]
1041 noteNewRecentDocumentURL: o_true_file];
1044 vlc_object_release( p_playlist );
1048 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1051 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1053 if( p_playlist == NULL )
1058 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1060 playlist_item_t *p_item;
1061 NSDictionary *o_one_item;
1064 o_one_item = [o_array objectAtIndex: i_item];
1065 p_item = [self createItem: o_one_item];
1072 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1074 if( i_item == 0 && !b_enqueue )
1076 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1079 vlc_object_release( p_playlist );
1082 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1085 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1087 if( p_playlist == NULL )
1092 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1094 playlist_item_t *p_item;
1095 NSDictionary *o_one_item;
1098 o_one_item = [o_array objectAtIndex: i_item];
1099 p_item = [self createItem: o_one_item];
1106 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1108 if( i_item == 0 && !b_enqueue )
1110 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1113 vlc_object_release( p_playlist );
1117 - (IBAction)handlePopUp:(id)sender
1120 intf_thread_t * p_intf = VLCIntf;
1121 vlc_value_t val1,val2;
1122 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1124 if( p_playlist == NULL )
1129 switch( [o_loop_popup indexOfSelectedItem] )
1134 var_Set( p_playlist, "loop", val1 );
1136 var_Set( p_playlist, "repeat", val1 );
1137 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1142 var_Set( p_playlist, "repeat", val1 );
1144 var_Set( p_playlist, "loop", val1 );
1145 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1149 var_Get( p_playlist, "repeat", &val1 );
1150 var_Get( p_playlist, "loop", &val2 );
1151 if( val1.b_bool || val2.b_bool )
1154 var_Set( p_playlist, "repeat", val1 );
1155 var_Set( p_playlist, "loop", val1 );
1156 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1160 vlc_object_release( p_playlist );
1161 [self playlistUpdated];
1164 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1166 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1168 playlist_item_t *p_selected_item;
1169 int i_current, i_selected_row;
1174 i_selected_row = [o_outline_view selectedRow];
1175 if (i_selected_row < 0)
1178 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1179 i_selected_row] pointerValue];
1181 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1184 NSString *o_current_name, *o_current_author;
1186 vlc_mutex_lock( &p_playlist->object_lock );
1187 o_current_name = [NSString stringWithUTF8String:
1188 p_item->pp_children[i_current]->input.psz_name];
1189 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1190 _("Meta-information"),_("Artist") );
1191 o_current_author = [NSString stringWithUTF8String: psz_temp];
1193 vlc_mutex_unlock( &p_playlist->object_lock );
1195 if( p_selected_item == p_item->pp_children[i_current] &&
1196 b_selected_item_met == NO )
1198 b_selected_item_met = YES;
1200 else if( p_selected_item == p_item->pp_children[i_current] &&
1201 b_selected_item_met == YES )
1203 vlc_object_release( p_playlist );
1206 else if( b_selected_item_met == YES &&
1207 ( [o_current_name rangeOfString:[o_search_field
1208 stringValue] options:NSCaseInsensitiveSearch ].length ||
1209 [o_current_author rangeOfString:[o_search_field
1210 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1212 vlc_object_release( p_playlist );
1213 /*Adds the parent items in the result array as well, so that we can
1215 return [NSMutableArray arrayWithObject: [NSValue
1216 valueWithPointer: p_item->pp_children[i_current]]];
1218 if( p_item->pp_children[i_current]->i_children > 0 )
1220 id o_result = [self subSearchItem:
1221 p_item->pp_children[i_current]];
1222 if( o_result != NULL )
1224 vlc_object_release( p_playlist );
1225 [o_result insertObject: [NSValue valueWithPointer:
1226 p_item->pp_children[i_current]] atIndex:0];
1231 vlc_object_release( p_playlist );
1235 - (IBAction)searchItem:(id)sender
1237 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1239 playlist_view_t * p_view;
1245 b_selected_item_met = NO;
1247 if( p_playlist == NULL )
1249 p_view = playlist_ViewFind( p_playlist, i_current_view );
1253 /*First, only search after the selected item:*
1254 *(b_selected_item_met = NO) */
1255 o_result = [self subSearchItem:p_view->p_root];
1256 if( o_result == NULL )
1258 /* If the first search failed, search again from the beginning */
1259 o_result = [self subSearchItem:p_view->p_root];
1261 if( o_result != NULL )
1264 if( [[o_result objectAtIndex: 0] pointerValue] ==
1265 p_playlist->p_general )
1270 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1272 [o_outline_view expandItem: [o_outline_dict objectForKey:
1273 [NSString stringWithFormat: @"%p",
1274 [[o_result objectAtIndex: i] pointerValue]]]];
1276 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1277 [NSString stringWithFormat: @"%p",
1278 [[o_result objectAtIndex: [o_result count] - 1 ]
1283 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1284 [o_outline_view scrollRowToVisible: i_row];
1287 vlc_object_release( p_playlist );
1290 - (IBAction)recursiveExpandNode:(id)sender
1293 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1294 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1296 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1297 isItemExpandable: o_item] )
1299 for( i = 0 ; i < p_item->i_parents ; i++ )
1301 if( p_item->pp_parents[i]->i_view == i_current_view )
1303 o_item = [o_outline_dict objectForKey: [NSString
1304 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1310 /* We need to collapse the node first, since OSX refuses to recursively
1311 expand an already expanded node, even if children nodes are collapsed. */
1312 [o_outline_view collapseItem: o_item collapseChildren: YES];
1313 [o_outline_view expandItem: o_item expandChildren: YES];
1316 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1320 vlc_bool_t b_item_sel;
1322 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1324 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1325 [o_outline_view selectedRow] != -1 );
1326 b_rows = [o_outline_view numberOfRows] != 0;
1328 [o_mi_play setEnabled: b_item_sel];
1329 [o_mi_delete setEnabled: b_item_sel];
1330 [o_mi_selectall setEnabled: b_rows];
1331 [o_mi_info setEnabled: b_item_sel];
1332 [o_mi_recursive_expand setEnabled: b_item_sel];
1333 [o_mi_sort_name setEnabled: b_item_sel];
1334 [o_mi_sort_author setEnabled: b_item_sel];
1336 return( o_ctx_menu );
1339 - (void)outlineView: (NSTableView*)o_tv
1340 didClickTableColumn:(NSTableColumn *)o_tc
1342 int i_mode = 0, i_type;
1343 intf_thread_t *p_intf = VLCIntf;
1344 playlist_view_t *p_view;
1346 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1348 if( p_playlist == NULL )
1353 /* Check whether the selected table column header corresponds to a
1354 sortable table column*/
1355 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1357 vlc_object_release( p_playlist );
1361 p_view = playlist_ViewFind( p_playlist, i_current_view );
1363 if( o_tc_sortColumn == o_tc )
1365 b_isSortDescending = !b_isSortDescending;
1369 b_isSortDescending = VLC_FALSE;
1372 if( o_tc == o_tc_name )
1374 i_mode = SORT_TITLE;
1376 else if( o_tc == o_tc_author )
1378 i_mode = SORT_AUTHOR;
1381 if( b_isSortDescending )
1383 i_type = ORDER_REVERSE;
1387 i_type = ORDER_NORMAL;
1390 vlc_mutex_lock( &p_playlist->object_lock );
1391 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1392 vlc_mutex_unlock( &p_playlist->object_lock );
1394 vlc_object_release( p_playlist );
1395 [self playlistUpdated];
1397 o_tc_sortColumn = o_tc;
1398 [o_outline_view setHighlightedTableColumn:o_tc];
1400 if( b_isSortDescending )
1402 [o_outline_view setIndicatorImage:o_descendingSortingImage
1403 inTableColumn:o_tc];
1407 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1408 inTableColumn:o_tc];
1413 - (void)outlineView:(NSOutlineView *)outlineView
1414 willDisplayCell:(id)cell
1415 forTableColumn:(NSTableColumn *)tableColumn
1418 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1423 if( !p_playlist ) return;
1425 o_playing_item = [o_outline_dict objectForKey:
1426 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1428 if( [self isItem: [o_playing_item pointerValue] inNode:
1429 [item pointerValue] checkItemExistence: YES]
1430 || [o_playing_item isEqual: item] )
1432 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1436 [cell setFont: [NSFont systemFontOfSize: 0]];
1438 vlc_object_release( p_playlist );
1443 @implementation VLCPlaylist (NSOutlineViewDataSource)
1445 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1447 id o_value = [super outlineView: outlineView child: index ofItem: item];
1448 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1451 if( !p_playlist ) return nil;
1453 if( p_playlist->i_size >= 2 )
1455 [o_status_field setStringValue: [NSString stringWithFormat:
1456 _NS("%i items in playlist"), p_playlist->i_size]];
1460 if( p_playlist->i_size == 0 )
1462 [o_status_field setStringValue: [NSString stringWithFormat:
1463 _NS("no items in playlist"), p_playlist->i_size]];
1467 [o_status_field setStringValue: [NSString stringWithFormat:
1468 _NS("1 item in playlist"), p_playlist->i_size]];
1471 vlc_object_release( p_playlist );
1473 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1474 [o_value pointerValue]]];
1480 /* Required for drag & drop and reordering */
1481 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1484 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1487 /* First remove the items that were moved during the last drag & drop
1489 [o_items_array removeAllObjects];
1490 [o_nodes_array removeAllObjects];
1492 if( !p_playlist ) return NO;
1494 for( i = 0 ; i < [items count] ; i++ )
1496 id o_item = [items objectAtIndex: i];
1498 /* Refuse to move items that are not in the General Node
1499 (Service Discovery) */
1500 if( ![self isItem: [o_item pointerValue] inNode:
1501 p_playlist->p_general checkItemExistence: NO])
1503 vlc_object_release(p_playlist);
1506 /* Fill the items and nodes to move in 2 different arrays */
1507 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1508 [o_nodes_array addObject: o_item];
1510 [o_items_array addObject: o_item];
1513 /* Now we need to check if there are selected items that are in already
1514 selected nodes. In that case, we only want to move the nodes */
1515 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1516 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1519 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1521 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1523 if( j == i ) continue;
1524 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1525 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1527 [o_nodes_array removeObjectAtIndex:i];
1528 /* We need to execute the next iteration with the same index
1529 since the current item has been deleted */
1536 for( i = 0 ; i < [o_items_array count] ; i++ )
1538 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1540 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1541 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1543 [o_items_array removeObjectAtIndex:i];
1550 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1551 a Drop operation comçing from the playlist.
1552 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1555 [pboard declareTypes: [NSArray arrayWithObjects:
1556 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1557 [pboard setPropertyList:[NSArray array]
1558 forType:NSFilenamesPboardType];
1560 vlc_object_release(p_playlist);
1564 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1566 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1568 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1570 if( !p_playlist ) return NSDragOperationNone;
1572 /* We refuse to drop an item in anything else than a child of the General
1573 Node. We still accept items that would be root nodes of the outlineview
1574 however, to allow drop in an empty playlist.*/
1575 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1576 checkItemExistence: NO] || item == nil) )
1578 vlc_object_release(p_playlist);
1579 return NSDragOperationNone;
1582 /* Drop from the Playlist */
1583 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1586 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1588 /* We refuse to Drop in a child of an item we are moving */
1589 if( [self isItem: [item pointerValue] inNode:
1590 [[o_nodes_array objectAtIndex: i] pointerValue]
1591 checkItemExistence: NO] )
1593 vlc_object_release(p_playlist);
1594 return NSDragOperationNone;
1597 vlc_object_release(p_playlist);
1598 return NSDragOperationMove;
1601 /* Drop from the Finder */
1602 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1604 vlc_object_release(p_playlist);
1605 return NSDragOperationGeneric;
1607 vlc_object_release(p_playlist);
1608 return NSDragOperationNone;
1611 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1613 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1615 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1617 if( !p_playlist ) return NO;
1619 /* Drag & Drop inside the playlist */
1620 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1624 playlist_item_t *p_new_parent, *p_item = NULL;
1625 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1627 /* If the item is to be dropped as root item of the outline, make it a
1628 child of the General node.
1629 Else, choose the proposed parent as parent. */
1631 p_new_parent = p_playlist->p_general;
1633 p_new_parent = [item pointerValue];
1635 /* If the proposed parent is not a node, then use the parent node of
1637 if( p_new_parent->i_children <= 0 )
1640 playlist_item_t *p_temp_item = p_new_parent;
1641 p_new_parent = [self parentOfItem: p_new_parent];
1644 vlc_object_release(p_playlist);
1647 /* Calculate the position of the dropped item in this new parent:
1648 following the first proposed parent. */
1649 for( j = 0; j < p_new_parent->i_children; j++ )
1651 if( p_new_parent->pp_children[j] == p_temp_item )
1656 else if( j == p_new_parent->i_children - 1 )
1661 for( i = 0; i < [o_all_items count]; i++ )
1663 playlist_item_t *p_old_parent = NULL;
1664 int i_old_index = 0;
1666 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1667 p_old_parent = [self parentOfItem: p_item];
1670 /* We may need the old index later */
1671 if( p_new_parent == p_old_parent )
1674 for( j = 0; j < p_old_parent->i_children; j++ )
1676 if( p_old_parent->pp_children[j] == p_item )
1685 /* If we move the playing item in a different node or we move the
1686 node containing the playing item in a different node, then stop
1687 playback, or the playlist refuses to detach the item. */
1688 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1689 (( p_item == p_playlist->status.p_item &&
1690 p_new_parent != p_old_parent) ||
1691 ( p_item->i_children > 0 &&
1692 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1694 playlist_Stop( p_playlist );
1696 vlc_mutex_lock( &p_playlist->object_lock );
1697 // Acually detach the item from the old position
1698 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1700 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1704 /* Calculate the new index */
1707 /* If we move the item in the same node, we need to take into
1708 account that one item will be deleted */
1709 else if((p_new_parent == p_old_parent &&
1710 i_old_index < index + (int)i)
1711 || p_new_parent == p_playlist->p_general || index == 0 )
1712 i_new_index = index + i;
1714 i_new_index = index + i + 1;
1715 // Reattach the item to the new position
1716 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1717 p_new_parent, i_new_index );
1719 vlc_mutex_unlock( &p_playlist->object_lock );
1721 [self playlistUpdated];
1722 i_row = [o_outline_view rowForItem:[o_outline_dict
1723 objectForKey:[NSString stringWithFormat: @"%p",
1724 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1728 i_row = [o_outline_view rowForItem:[o_outline_dict
1729 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1732 [o_outline_view deselectAll: self];
1733 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1734 [o_outline_view scrollRowToVisible: i_row];
1736 vlc_object_release(p_playlist);
1740 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1743 playlist_item_t *p_node = [item pointerValue];
1745 NSArray *o_array = [NSArray array];
1746 NSArray *o_values = [[o_pasteboard propertyListForType:
1747 NSFilenamesPboardType]
1748 sortedArrayUsingSelector:
1749 @selector(caseInsensitiveCompare:)];
1751 for( i = 0; i < (int)[o_values count]; i++)
1753 NSDictionary *o_dic;
1754 o_dic = [NSDictionary dictionaryWithObject:[o_values
1755 objectAtIndex:i] forKey:@"ITEM_URL"];
1756 o_array = [o_array arrayByAddingObject: o_dic];
1761 [self appendArray: o_array atPos: index enqueue: YES];
1763 else if( p_node->i_children == -1 )
1766 playlist_item_t *p_real_node = NULL;
1768 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1770 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1772 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1775 if( i_counter == p_node->i_parents )
1777 vlc_object_release(p_playlist);
1781 [self appendNodeArray: o_array inNode: p_real_node
1782 atPos: index inView: i_current_view enqueue: YES];
1786 [self appendNodeArray: o_array inNode: p_node
1787 atPos: index inView: i_current_view enqueue: YES];
1789 vlc_object_release( p_playlist );
1792 vlc_object_release( p_playlist );
1796 /* Delegate method of NSWindow */
1797 /*- (void)windowWillClose:(NSNotification *)aNotification
1799 [o_btn_playlist setState: NSOffState];