1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videola/n dot org>
9 * Benjamin Pracht <bigben at videolab dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
111 o_outline_dict = [[NSMutableDictionary alloc] init];
117 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
119 i_current_view = VIEW_CATEGORY;
120 playlist_ViewUpdate( p_playlist, i_current_view );
122 [o_outline_view setTarget: self];
123 [o_outline_view setDelegate: self];
124 [o_outline_view setDataSource: self];
126 vlc_object_release( p_playlist );
132 [[o_tc_name headerCell] setStringValue:_NS("Name")];
133 [[o_tc_author headerCell] setStringValue:_NS("Author")];
134 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
137 - (NSOutlineView *)outlineView
139 return o_outline_view;
142 - (playlist_item_t *)selectedPlaylistItem
144 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
150 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
152 /* return the number of children for Obj-C pointer item */ /* DONE */
153 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
156 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
158 if( p_playlist == NULL )
160 if( outlineView != o_outline_view )
162 vlc_object_release( p_playlist );
169 playlist_view_t *p_view;
170 p_view = playlist_ViewFind( p_playlist, i_current_view );
171 if( p_view && p_view->p_root )
173 i_return = p_view->p_root->i_children;
175 if( i_current_view == VIEW_CATEGORY )
177 i_return--; /* remove the GENERAL item from the list */
178 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
184 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
186 i_return = p_item->i_children;
188 vlc_object_release( p_playlist );
196 /* return the child at index for the Obj-C pointer item */ /* DONE */
197 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
199 playlist_item_t *p_return = NULL;
200 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
204 if( p_playlist == NULL )
210 playlist_view_t *p_view;
211 p_view = playlist_ViewFind( p_playlist, i_current_view );
212 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
214 if( i_current_view == VIEW_CATEGORY )
216 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
218 p_return = p_playlist->p_general->pp_children[index];
220 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
222 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
228 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
229 if( p_item && index < p_item->i_children && index >= 0 )
230 p_return = p_item->pp_children[index];
234 vlc_object_release( p_playlist );
236 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
239 o_value = [[NSValue valueWithPointer: p_return] retain];
244 /* is the item expandable */
245 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
248 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
250 if( p_playlist == NULL )
256 playlist_view_t *p_view;
257 p_view = playlist_ViewFind( p_playlist, i_current_view );
258 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
260 if( i_current_view == VIEW_CATEGORY )
263 i_return += p_playlist->p_general->i_children;
268 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
270 i_return = p_item->i_children;
272 vlc_object_release( p_playlist );
280 /* retrieve the string values for the cells */
281 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
284 intf_thread_t *p_intf = VLCIntf;
285 playlist_t *p_playlist;
286 playlist_item_t *p_item;
288 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
290 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
292 if( p_playlist == NULL )
297 p_item = (playlist_item_t *)[item pointerValue];
301 vlc_object_release( p_playlist );
305 if( [[o_tc identifier] isEqualToString:@"1"] )
307 o_value = [NSString stringWithUTF8String:
308 p_item->input.psz_name];
309 if( o_value == NULL )
310 o_value = [NSString stringWithCString:
311 p_item->input.psz_name];
313 else if( [[o_tc identifier] isEqualToString:@"2"] )
316 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
318 if( psz_temp == NULL )
322 o_value = [NSString stringWithUTF8String: psz_temp];
323 if( o_value == NULL )
325 o_value = [NSString stringWithCString: psz_temp];
330 else if( [[o_tc identifier] isEqualToString:@"3"] )
332 char psz_duration[MSTRTIME_MAX_SIZE];
333 mtime_t dur = p_item->input.i_duration;
336 secstotimestr( psz_duration, dur/1000000 );
337 o_value = [NSString stringWithUTF8String: psz_duration];
341 o_value = @"-:--:--";
344 vlc_object_release( p_playlist );
351 /*****************************************************************************
352 * VLCPlaylistWizard implementation
353 *****************************************************************************/
354 @implementation VLCPlaylistWizard
356 - (IBAction)reloadOutlineView
358 /* Only reload the outlineview if the wizard window is open since this can
359 be quite long on big playlists */
360 if( [[o_outline_view window] isVisible] )
362 [o_outline_view reloadData];
368 /*****************************************************************************
369 * VLCPlaylist implementation
370 *****************************************************************************/
371 @implementation VLCPlaylist
378 o_nodes_array = [[NSMutableArray alloc] init];
379 o_items_array = [[NSMutableArray alloc] init];
386 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
388 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
393 [super awakeFromNib];
395 [o_outline_view setDoubleAction: @selector(playItem:)];
397 [o_outline_view registerForDraggedTypes:
398 [NSArray arrayWithObjects: NSFilenamesPboardType,
399 @"VLCPlaylistItemPboardType", nil]];
400 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
402 /* We need to check whether _defaultTableHeaderSortImage exists, since it
403 belongs to an Apple hidden private API, and then can "disapear" at any time*/
405 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
407 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
411 o_ascendingSortingImage = nil;
414 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
416 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
420 o_descendingSortingImage = nil;
423 o_tc_sortColumn = nil;
425 for( i_index = 0; i_index < p_list->i_count; i_index++ )
428 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
430 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
432 /* create the menu entries used in the playlist menu */
433 o_lmi = [[o_mi_services submenu] addItemWithTitle:
434 [NSString stringWithUTF8String:
435 p_parser->psz_longname ? p_parser->psz_longname :
436 ( p_parser->psz_shortname ? p_parser->psz_shortname:
437 p_parser->psz_object_name)]
438 action: @selector(servicesChange:)
440 [o_lmi setTarget: self];
441 [o_lmi setRepresentedObject:
442 [NSString stringWithCString: p_parser->psz_object_name]];
443 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
444 p_parser->psz_object_name ) )
445 [o_lmi setState: NSOnState];
447 /* create the menu entries for the main menu */
448 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
449 [NSString stringWithUTF8String:
450 p_parser->psz_longname ? p_parser->psz_longname :
451 ( p_parser->psz_shortname ? p_parser->psz_shortname:
452 p_parser->psz_object_name)]
453 action: @selector(servicesChange:)
455 [o_lmi setTarget: self];
456 [o_lmi setRepresentedObject:
457 [NSString stringWithCString: p_parser->psz_object_name]];
458 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
459 p_parser->psz_object_name ) )
460 [o_lmi setState: NSOnState];
463 vlc_list_release( p_list );
464 vlc_object_release( p_playlist );
466 /* Change the simple textfield into a searchField if we can... */
468 if( MACOS_VERSION >= 10.3 )
470 NSView *o_parentview = [o_status_field superview];
471 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
472 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
473 [o_better_search_field setDelegate:self];
474 [[NSNotificationCenter defaultCenter] addObserver: self
475 selector: @selector(searchfieldChanged:)
476 name: NSControlTextDidChangeNotification
477 object: o_better_search_field];
479 [o_better_search_field setTarget:self];
480 [o_better_search_field setAction:@selector(searchItem:)];
482 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
483 [o_parentview addSubview:o_better_search_field];
484 [o_search_field setHidden:YES];
487 //[self playlistUpdated];
490 - (void)searchfieldChanged:(NSNotification *)o_notification
492 [o_search_field setStringValue:[[o_notification object] stringValue]];
499 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
500 [o_mi_play setTitle: _NS("Play")];
501 [o_mi_delete setTitle: _NS("Delete")];
502 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
503 [o_mi_selectall setTitle: _NS("Select All")];
504 [o_mi_info setTitle: _NS("Properties")];
505 [o_mi_preparse setTitle: _NS("Preparse")];
506 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
507 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
508 [o_mi_services setTitle: _NS("Services discovery")];
509 [o_status_field setStringValue: [NSString stringWithFormat:
510 _NS("no items in playlist")]];
512 [o_random_ckb setTitle: _NS("Random")];
514 [o_search_button setTitle: _NS("Search")];
516 [o_search_field setToolTip: _NS("Search in Playlist")];
517 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
518 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
519 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
522 - (void)playlistUpdated
526 /* Clear indications of any existing column sorting*/
527 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
529 [o_outline_view setIndicatorImage:nil inTableColumn:
530 [[o_outline_view tableColumns] objectAtIndex:i]];
533 [o_outline_view setHighlightedTableColumn:nil];
534 o_tc_sortColumn = nil;
535 // TODO Find a way to keep the dict size to a minimum
536 //[o_outline_dict removeAllObjects];
537 [o_outline_view reloadData];
538 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
539 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
542 - (void)playModeUpdated
544 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
546 vlc_value_t val, val2;
548 if( p_playlist == NULL )
553 var_Get( p_playlist, "loop", &val2 );
554 var_Get( p_playlist, "repeat", &val );
555 if( val.b_bool == VLC_TRUE )
557 [o_loop_popup selectItemAtIndex: 1];
559 else if( val2.b_bool == VLC_TRUE )
561 [o_loop_popup selectItemAtIndex: 2];
565 [o_loop_popup selectItemAtIndex: 0];
568 var_Get( p_playlist, "random", &val );
569 [o_random_ckb setState: val.b_bool];
571 vlc_object_release( p_playlist );
574 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
577 for( i = 0 ; i < p_item->i_parents; i++ )
579 if( p_item->pp_parents[i]->i_view == i_current_view )
581 return p_item->pp_parents[i]->p_parent;
587 - (void)updateRowSelection
593 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
595 playlist_item_t *p_item, *p_temp_item;
596 NSMutableArray *o_array = [NSMutableArray array];
598 if( p_playlist == NULL )
601 p_item = p_playlist->status.p_item;
604 vlc_object_release(p_playlist);
608 p_temp_item = p_item;
609 while( p_temp_item->i_parents > 0 )
611 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
613 p_temp_item = [self parentOfItem: p_temp_item];
614 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
616 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
618 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
624 for (j = 0 ; j < [o_array count] - 1 ; j++)
627 if( ( o_item = [o_outline_dict objectForKey:
628 [NSString stringWithFormat: @"%p",
629 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
630 [o_outline_view expandItem: o_item];
634 i_row = [o_outline_view rowForItem:[o_outline_dict
635 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
637 [o_outline_view selectRow: i_row byExtendingSelection: NO];
638 [o_outline_view scrollRowToVisible: i_row];
640 vlc_object_release(p_playlist);
643 /* Check if p_item is a child of p_node recursively. We need to check the item
644 existence first since OSX sometimes tries to redraw items that have been
645 deleted. We don't do it when not required since this verification takes
646 quite a long time on big playlists (yes, pretty hacky). */
647 - (BOOL)isItem: (playlist_item_t *)p_item
648 inNode: (playlist_item_t *)p_node
649 checkItemExistence:(BOOL)b_check
652 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
654 playlist_item_t *p_temp_item = p_item;
656 if( p_playlist == NULL )
661 if( p_node == p_item )
663 vlc_object_release(p_playlist);
667 if( p_node->i_children < 1)
669 vlc_object_release(p_playlist);
676 vlc_mutex_lock( &p_playlist->object_lock );
680 /* Since outlineView: willDisplayCell:... may call this function with
681 p_items that don't exist anymore, first check if the item is still
682 in the playlist. Any cleaner solution welcomed. */
683 for( i = 0; i < p_playlist->i_all_size; i++ )
685 if( p_playlist->pp_all_items[i] == p_item ) break;
686 else if ( i == p_playlist->i_all_size - 1 )
688 vlc_object_release( p_playlist );
689 vlc_mutex_unlock( &p_playlist->object_lock );
695 while( p_temp_item->i_parents > 0 )
697 p_temp_item = [self parentOfItem: p_temp_item];
698 if( p_temp_item == p_node )
700 vlc_mutex_unlock( &p_playlist->object_lock );
701 vlc_object_release( p_playlist );
705 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
707 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
709 if( p_temp_item->pp_parents[i]->p_parent == p_node )
711 vlc_mutex_unlock( &p_playlist->object_lock );
712 vlc_object_release( p_playlist );
717 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
723 vlc_mutex_unlock( &p_playlist->object_lock );
726 vlc_object_release( p_playlist );
730 /* This method is usefull for instance to remove the selected children of an
731 already selected node */
732 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
735 for( i = 0 ; i < [o_items count] ; i++ )
737 for ( j = 0 ; j < [o_nodes count] ; j++ )
739 if( o_items == o_nodes)
741 if( j == i ) continue;
743 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
744 inNode: [[o_nodes objectAtIndex:j] pointerValue]
745 checkItemExistence: NO] )
747 [o_items removeObjectAtIndex:i];
748 /* We need to execute the next iteration with the same index
749 since the current item has been deleted */
758 - (IBAction)savePlaylist:(id)sender
760 intf_thread_t * p_intf = VLCIntf;
761 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
764 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
765 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
766 [o_save_panel setTitle: _NS("Save Playlist")];
767 [o_save_panel setPrompt: _NS("Save")];
769 if( [o_save_panel runModalForDirectory: nil
770 file: o_name] == NSOKButton )
772 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
774 vlc_object_release( p_playlist );
778 /* When called retrieves the selected outlineview row and plays that node or item */
779 - (IBAction)playItem:(id)sender
781 intf_thread_t * p_intf = VLCIntf;
782 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
785 if( p_playlist != NULL )
787 playlist_item_t *p_item;
788 playlist_item_t *p_node = NULL;
791 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
795 if( p_item->i_children == -1 )
797 p_node = [self parentOfItem: p_item];
799 /* for( i = 0 ; i < p_item->i_parents ; i++ )
801 if( p_item->pp_parents[i]->i_view == i_current_view )
803 p_node = p_item->pp_parents[i]->p_parent;
810 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
812 p_item = p_node->pp_children[0];
819 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
821 vlc_object_release( p_playlist );
825 /* When called retrieves the selected outlineview row and plays that node or item */
826 - (IBAction)preparseItem:(id)sender
829 NSMutableArray *o_to_preparse;
830 intf_thread_t * p_intf = VLCIntf;
831 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
834 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
835 i_count = [o_to_preparse count];
837 if( p_playlist != NULL )
841 playlist_item_t *p_item = NULL;
843 for( i = 0; i < i_count; i++ )
845 o_number = [o_to_preparse lastObject];
846 i_row = [o_number intValue];
847 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
848 [o_to_preparse removeObject: o_number];
849 [o_outline_view deselectRow: i_row];
853 if( p_item->i_children == -1 )
855 playlist_PreparseEnqueue( p_playlist, &p_item->input );
859 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
863 vlc_object_release( p_playlist );
865 [self playlistUpdated];
868 - (IBAction)servicesChange:(id)sender
870 NSMenuItem *o_mi = (NSMenuItem *)sender;
871 NSString *o_string = [o_mi representedObject];
872 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
874 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
875 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
877 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
879 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
880 [o_string cString] ) ? YES : NO];
882 i_current_view = VIEW_CATEGORY;
883 playlist_ViewUpdate( p_playlist, i_current_view );
884 vlc_object_release( p_playlist );
885 [self playlistUpdated];
889 - (IBAction)selectAll:(id)sender
891 [o_outline_view selectAll: nil];
894 - (IBAction)deleteItem:(id)sender
896 int i, i_count, i_row;
897 NSMutableArray *o_to_delete;
900 playlist_t * p_playlist;
901 intf_thread_t * p_intf = VLCIntf;
903 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
906 if ( p_playlist == NULL )
910 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
911 i_count = [o_to_delete count];
913 for( i = 0; i < i_count; i++ )
915 o_number = [o_to_delete lastObject];
916 i_row = [o_number intValue];
917 id o_item = [o_outline_view itemAtRow: i_row];
918 playlist_item_t *p_item = [o_item pointerValue];
919 [o_to_delete removeObject: o_number];
920 [o_outline_view deselectRow: i_row];
922 if( [[o_outline_view dataSource] outlineView:o_outline_view
923 numberOfChildrenOfItem: o_item] > 0 )
924 //is a node and not an item
926 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
927 [self isItem: p_playlist->status.p_item inNode:
928 ((playlist_item_t *)[o_item pointerValue])
929 checkItemExistence: NO] == YES )
931 // if current item is in selected node and is playing then stop playlist
932 playlist_Stop( p_playlist );
934 vlc_mutex_lock( &p_playlist->object_lock );
935 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
936 vlc_mutex_unlock( &p_playlist->object_lock );
940 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
941 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
943 playlist_Stop( p_playlist );
945 vlc_mutex_lock( &p_playlist->object_lock );
946 playlist_Delete( p_playlist, p_item->input.i_id );
947 vlc_mutex_unlock( &p_playlist->object_lock );
950 [self playlistUpdated];
951 vlc_object_release( p_playlist );
954 - (IBAction)sortNodeByName:(id)sender
956 [self sortNode: SORT_TITLE];
959 - (IBAction)sortNodeByAuthor:(id)sender
961 [self sortNode: SORT_AUTHOR];
964 - (void)sortNode:(int)i_mode
966 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
968 playlist_item_t * p_item;
970 if (p_playlist == NULL)
975 if( [o_outline_view selectedRow] > -1 )
977 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
981 /*If no item is selected, sort the whole playlist*/
983 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
984 p_item = p_view->p_root;
987 if( p_item->i_children > -1 ) // the item is a node
989 vlc_mutex_lock( &p_playlist->object_lock );
990 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
991 vlc_mutex_unlock( &p_playlist->object_lock );
997 for( i = 0 ; i < p_item->i_parents ; i++ )
999 if( p_item->pp_parents[i]->i_view == i_current_view )
1001 vlc_mutex_lock( &p_playlist->object_lock );
1002 playlist_RecursiveNodeSort( p_playlist,
1003 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
1004 vlc_mutex_unlock( &p_playlist->object_lock );
1009 vlc_object_release( p_playlist );
1010 [self playlistUpdated];
1013 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
1015 intf_thread_t * p_intf = VLCIntf;
1016 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1019 if( p_playlist == NULL )
1023 playlist_item_t *p_item;
1025 BOOL b_rem = FALSE, b_dir = FALSE;
1026 NSString *o_uri, *o_name;
1031 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1032 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1033 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1035 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
1036 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1038 int i_count, i_index;
1039 struct statfs *mounts = NULL;
1041 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1042 /* getmntinfo returns a pointer to static data. Do not free. */
1043 for( i_index = 0 ; i_index < i_count; i_index++ )
1045 NSMutableString *o_temp, *o_temp2;
1046 o_temp = [NSMutableString stringWithString: o_uri];
1047 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1048 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1049 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1050 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1052 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1054 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1058 /* If no name, then make a guess */
1059 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1061 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1062 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1063 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1065 /* All of this is to make sure CD's play when you D&D them on VLC */
1066 /* Converts mountpoint to a /dev file */
1069 NSMutableString *o_temp;
1071 buf = (struct statfs *) malloc (sizeof(struct statfs));
1072 statfs( [o_uri fileSystemRepresentation], buf );
1073 psz_dev = strdup(buf->f_mntfromname);
1074 o_temp = [NSMutableString stringWithCString: psz_dev ];
1075 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1076 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1077 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1081 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1087 for( i = 0; i < (int)[o_options count]; i++ )
1089 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1093 /* Recent documents menu */
1094 o_true_file = [NSURL fileURLWithPath: o_uri];
1095 if( o_true_file != nil )
1097 [[NSDocumentController sharedDocumentController]
1098 noteNewRecentDocumentURL: o_true_file];
1101 vlc_object_release( p_playlist );
1105 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1108 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1110 if( p_playlist == NULL )
1115 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1117 playlist_item_t *p_item;
1118 NSDictionary *o_one_item;
1121 o_one_item = [o_array objectAtIndex: i_item];
1122 p_item = [self createItem: o_one_item];
1129 playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1131 if( i_item == 0 && !b_enqueue )
1133 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1136 vlc_object_release( p_playlist );
1139 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1142 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1144 if( p_playlist == NULL )
1149 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1151 playlist_item_t *p_item;
1152 NSDictionary *o_one_item;
1155 o_one_item = [o_array objectAtIndex: i_item];
1156 p_item = [self createItem: o_one_item];
1163 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
1165 if( i_item == 0 && !b_enqueue )
1167 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1170 vlc_object_release( p_playlist );
1174 - (IBAction)handlePopUp:(id)sender
1177 intf_thread_t * p_intf = VLCIntf;
1178 vlc_value_t val1,val2;
1179 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1181 if( p_playlist == NULL )
1186 switch( [o_loop_popup indexOfSelectedItem] )
1191 var_Set( p_playlist, "loop", val1 );
1193 var_Set( p_playlist, "repeat", val1 );
1194 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1199 var_Set( p_playlist, "repeat", val1 );
1201 var_Set( p_playlist, "loop", val1 );
1202 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1206 var_Get( p_playlist, "repeat", &val1 );
1207 var_Get( p_playlist, "loop", &val2 );
1208 if( val1.b_bool || val2.b_bool )
1211 var_Set( p_playlist, "repeat", val1 );
1212 var_Set( p_playlist, "loop", val1 );
1213 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1217 vlc_object_release( p_playlist );
1218 [self playlistUpdated];
1221 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1223 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1225 playlist_item_t *p_selected_item;
1226 int i_current, i_selected_row;
1231 i_selected_row = [o_outline_view selectedRow];
1232 if (i_selected_row < 0)
1235 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1236 i_selected_row] pointerValue];
1238 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1241 NSString *o_current_name, *o_current_author;
1243 vlc_mutex_lock( &p_playlist->object_lock );
1244 o_current_name = [NSString stringWithUTF8String:
1245 p_item->pp_children[i_current]->input.psz_name];
1246 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1247 _("Meta-information"),_("Artist") );
1248 o_current_author = [NSString stringWithUTF8String: psz_temp];
1250 vlc_mutex_unlock( &p_playlist->object_lock );
1252 if( p_selected_item == p_item->pp_children[i_current] &&
1253 b_selected_item_met == NO )
1255 b_selected_item_met = YES;
1257 else if( p_selected_item == p_item->pp_children[i_current] &&
1258 b_selected_item_met == YES )
1260 vlc_object_release( p_playlist );
1263 else if( b_selected_item_met == YES &&
1264 ( [o_current_name rangeOfString:[o_search_field
1265 stringValue] options:NSCaseInsensitiveSearch ].length ||
1266 [o_current_author rangeOfString:[o_search_field
1267 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1269 vlc_object_release( p_playlist );
1270 /*Adds the parent items in the result array as well, so that we can
1272 return [NSMutableArray arrayWithObject: [NSValue
1273 valueWithPointer: p_item->pp_children[i_current]]];
1275 if( p_item->pp_children[i_current]->i_children > 0 )
1277 id o_result = [self subSearchItem:
1278 p_item->pp_children[i_current]];
1279 if( o_result != NULL )
1281 vlc_object_release( p_playlist );
1282 [o_result insertObject: [NSValue valueWithPointer:
1283 p_item->pp_children[i_current]] atIndex:0];
1288 vlc_object_release( p_playlist );
1292 - (IBAction)searchItem:(id)sender
1294 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1296 playlist_view_t * p_view;
1302 b_selected_item_met = NO;
1304 if( p_playlist == NULL )
1306 p_view = playlist_ViewFind( p_playlist, i_current_view );
1310 /*First, only search after the selected item:*
1311 *(b_selected_item_met = NO) */
1312 o_result = [self subSearchItem:p_view->p_root];
1313 if( o_result == NULL )
1315 /* If the first search failed, search again from the beginning */
1316 o_result = [self subSearchItem:p_view->p_root];
1318 if( o_result != NULL )
1321 if( [[o_result objectAtIndex: 0] pointerValue] ==
1322 p_playlist->p_general )
1327 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1329 [o_outline_view expandItem: [o_outline_dict objectForKey:
1330 [NSString stringWithFormat: @"%p",
1331 [[o_result objectAtIndex: i] pointerValue]]]];
1333 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1334 [NSString stringWithFormat: @"%p",
1335 [[o_result objectAtIndex: [o_result count] - 1 ]
1340 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1341 [o_outline_view scrollRowToVisible: i_row];
1344 vlc_object_release( p_playlist );
1347 - (IBAction)recursiveExpandNode:(id)sender
1350 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1351 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1353 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1354 isItemExpandable: o_item] )
1356 for( i = 0 ; i < p_item->i_parents ; i++ )
1358 if( p_item->pp_parents[i]->i_view == i_current_view )
1360 o_item = [o_outline_dict objectForKey: [NSString
1361 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1367 /* We need to collapse the node first, since OSX refuses to recursively
1368 expand an already expanded node, even if children nodes are collapsed. */
1369 [o_outline_view collapseItem: o_item collapseChildren: YES];
1370 [o_outline_view expandItem: o_item expandChildren: YES];
1373 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1377 vlc_bool_t b_item_sel;
1379 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1381 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1382 [o_outline_view selectedRow] != -1 );
1383 b_rows = [o_outline_view numberOfRows] != 0;
1385 [o_mi_play setEnabled: b_item_sel];
1386 [o_mi_delete setEnabled: b_item_sel];
1387 [o_mi_selectall setEnabled: b_rows];
1388 [o_mi_info setEnabled: b_item_sel];
1389 [o_mi_preparse setEnabled: b_item_sel];
1390 [o_mi_recursive_expand setEnabled: b_item_sel];
1391 [o_mi_sort_name setEnabled: b_item_sel];
1392 [o_mi_sort_author setEnabled: b_item_sel];
1394 return( o_ctx_menu );
1397 - (void)outlineView: (NSTableView*)o_tv
1398 didClickTableColumn:(NSTableColumn *)o_tc
1400 int i_mode = 0, i_type;
1401 intf_thread_t *p_intf = VLCIntf;
1402 playlist_view_t *p_view;
1404 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1406 if( p_playlist == NULL )
1411 /* Check whether the selected table column header corresponds to a
1412 sortable table column*/
1413 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1415 vlc_object_release( p_playlist );
1419 p_view = playlist_ViewFind( p_playlist, i_current_view );
1421 if( o_tc_sortColumn == o_tc )
1423 b_isSortDescending = !b_isSortDescending;
1427 b_isSortDescending = VLC_FALSE;
1430 if( o_tc == o_tc_name )
1432 i_mode = SORT_TITLE;
1434 else if( o_tc == o_tc_author )
1436 i_mode = SORT_AUTHOR;
1439 if( b_isSortDescending )
1441 i_type = ORDER_REVERSE;
1445 i_type = ORDER_NORMAL;
1448 vlc_mutex_lock( &p_playlist->object_lock );
1449 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1450 vlc_mutex_unlock( &p_playlist->object_lock );
1452 vlc_object_release( p_playlist );
1453 [self playlistUpdated];
1455 o_tc_sortColumn = o_tc;
1456 [o_outline_view setHighlightedTableColumn:o_tc];
1458 if( b_isSortDescending )
1460 [o_outline_view setIndicatorImage:o_descendingSortingImage
1461 inTableColumn:o_tc];
1465 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1466 inTableColumn:o_tc];
1471 - (void)outlineView:(NSOutlineView *)outlineView
1472 willDisplayCell:(id)cell
1473 forTableColumn:(NSTableColumn *)tableColumn
1476 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1481 if( !p_playlist ) return;
1483 o_playing_item = [o_outline_dict objectForKey:
1484 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1486 if( [self isItem: [o_playing_item pointerValue] inNode:
1487 [item pointerValue] checkItemExistence: YES]
1488 || [o_playing_item isEqual: item] )
1490 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1494 [cell setFont: [NSFont systemFontOfSize: 0]];
1496 vlc_object_release( p_playlist );
1501 @implementation VLCPlaylist (NSOutlineViewDataSource)
1503 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1505 id o_value = [super outlineView: outlineView child: index ofItem: item];
1506 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1509 if( !p_playlist ) return nil;
1511 if( p_playlist->i_size >= 2 )
1513 [o_status_field setStringValue: [NSString stringWithFormat:
1514 _NS("%i items in playlist"), p_playlist->i_size]];
1518 if( p_playlist->i_size == 0 )
1520 [o_status_field setStringValue: [NSString stringWithFormat:
1521 _NS("no items in playlist"), p_playlist->i_size]];
1525 [o_status_field setStringValue: [NSString stringWithFormat:
1526 _NS("1 item in playlist"), p_playlist->i_size]];
1529 vlc_object_release( p_playlist );
1531 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1532 [o_value pointerValue]]];
1538 /* Required for drag & drop and reordering */
1539 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1542 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1545 /* First remove the items that were moved during the last drag & drop
1547 [o_items_array removeAllObjects];
1548 [o_nodes_array removeAllObjects];
1550 if( !p_playlist ) return NO;
1552 for( i = 0 ; i < [items count] ; i++ )
1554 id o_item = [items objectAtIndex: i];
1556 /* Refuse to move items that are not in the General Node
1557 (Service Discovery) */
1558 if( ![self isItem: [o_item pointerValue] inNode:
1559 p_playlist->p_general checkItemExistence: NO])
1561 vlc_object_release(p_playlist);
1564 /* Fill the items and nodes to move in 2 different arrays */
1565 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1566 [o_nodes_array addObject: o_item];
1568 [o_items_array addObject: o_item];
1571 /* Now we need to check if there are selected items that are in already
1572 selected nodes. In that case, we only want to move the nodes */
1573 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1574 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1576 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1577 a Drop operation coming from the playlist. */
1579 [pboard declareTypes: [NSArray arrayWithObjects:
1580 @"VLCPlaylistItemPboardType", nil] owner: self];
1581 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1583 vlc_object_release(p_playlist);
1587 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1589 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1591 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1593 if( !p_playlist ) return NSDragOperationNone;
1595 /* Dropping ON items is not allowed */
1596 if( index == NSOutlineViewDropOnItemIndex )
1598 vlc_object_release( p_playlist );
1599 return NSDragOperationNone;
1602 /* We refuse to drop an item in anything else than a child of the General
1603 Node. We still accept items that would be root nodes of the outlineview
1604 however, to allow drop in an empty playlist. */
1605 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1606 checkItemExistence: NO] || item == nil) )
1608 vlc_object_release( p_playlist );
1609 return NSDragOperationNone;
1612 /* Drop from the Playlist */
1613 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1616 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1618 /* We refuse to Drop in a child of an item we are moving */
1619 if( [self isItem: [item pointerValue] inNode:
1620 [[o_nodes_array objectAtIndex: i] pointerValue]
1621 checkItemExistence: NO] )
1623 vlc_object_release( p_playlist );
1624 return NSDragOperationNone;
1627 vlc_object_release(p_playlist);
1628 return NSDragOperationMove;
1631 /* Drop from the Finder */
1632 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1634 vlc_object_release(p_playlist);
1635 return NSDragOperationGeneric;
1637 vlc_object_release(p_playlist);
1638 return NSDragOperationNone;
1641 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1643 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1645 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1647 if( !p_playlist ) return NO;
1649 /* Drag & Drop inside the playlist */
1650 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1654 playlist_item_t *p_new_parent, *p_item = NULL;
1655 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1657 /* If the item is to be dropped as root item of the outline, make it a
1658 child of the General node.
1659 Else, choose the proposed parent as parent. */
1661 p_new_parent = p_playlist->p_general;
1663 p_new_parent = [item pointerValue];
1665 /* If the proposed parent is not a node, then use the parent node of
1667 if( p_new_parent->i_children <= 0 )
1670 playlist_item_t *p_temp_item = p_new_parent;
1671 p_new_parent = [self parentOfItem: p_new_parent];
1674 vlc_object_release(p_playlist);
1677 /* Calculate the position of the dropped item in this new parent:
1678 following the first proposed parent. */
1679 for( j = 0; j < p_new_parent->i_children; j++ )
1681 if( p_new_parent->pp_children[j] == p_temp_item )
1686 else if( j == p_new_parent->i_children - 1 )
1691 for( i = 0; i < [o_all_items count]; i++ )
1693 playlist_item_t *p_old_parent = NULL;
1694 int i_old_index = 0;
1696 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1697 p_old_parent = [self parentOfItem: p_item];
1700 /* We may need the old index later */
1701 if( p_new_parent == p_old_parent )
1704 for( j = 0; j < p_old_parent->i_children; j++ )
1706 if( p_old_parent->pp_children[j] == p_item )
1715 /* If we move the playing item in a different node or we move the
1716 node containing the playing item in a different node, then stop
1717 playback, or the playlist refuses to detach the item. */
1718 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1719 (( p_item == p_playlist->status.p_item &&
1720 p_new_parent != p_old_parent) ||
1721 ( p_item->i_children > 0 &&
1722 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1724 playlist_Stop( p_playlist );
1726 vlc_mutex_lock( &p_playlist->object_lock );
1727 // Acually detach the item from the old position
1728 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1730 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1734 /* Calculate the new index */
1737 /* If we move the item in the same node, we need to take into
1738 account that one item will be deleted */
1739 else if((p_new_parent == p_old_parent &&
1740 i_old_index < index + (int)i)
1741 || p_new_parent == p_playlist->p_general || index == 0 )
1742 i_new_index = index + i;
1744 i_new_index = index + i + 1;
1745 // Reattach the item to the new position
1746 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1747 p_new_parent, i_new_index );
1749 vlc_mutex_unlock( &p_playlist->object_lock );
1751 [self playlistUpdated];
1752 i_row = [o_outline_view rowForItem:[o_outline_dict
1753 objectForKey:[NSString stringWithFormat: @"%p",
1754 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1758 i_row = [o_outline_view rowForItem:[o_outline_dict
1759 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1762 [o_outline_view deselectAll: self];
1763 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1764 [o_outline_view scrollRowToVisible: i_row];
1766 vlc_object_release(p_playlist);
1770 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1773 playlist_item_t *p_node = [item pointerValue];
1775 NSArray *o_array = [NSArray array];
1776 NSArray *o_values = [[o_pasteboard propertyListForType:
1777 NSFilenamesPboardType]
1778 sortedArrayUsingSelector:
1779 @selector(caseInsensitiveCompare:)];
1781 for( i = 0; i < (int)[o_values count]; i++)
1783 NSDictionary *o_dic;
1784 o_dic = [NSDictionary dictionaryWithObject:[o_values
1785 objectAtIndex:i] forKey:@"ITEM_URL"];
1786 o_array = [o_array arrayByAddingObject: o_dic];
1791 [self appendArray: o_array atPos: index enqueue: YES];
1793 else if( p_node->i_children == -1 )
1796 playlist_item_t *p_real_node = NULL;
1798 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1800 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1802 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1805 if( i_counter == p_node->i_parents )
1807 vlc_object_release(p_playlist);
1811 [self appendNodeArray: o_array inNode: p_real_node
1812 atPos: index inView: i_current_view enqueue: YES];
1816 [self appendNodeArray: o_array inNode: p_node
1817 atPos: index inView: i_current_view enqueue: YES];
1819 vlc_object_release( p_playlist );
1822 vlc_object_release( p_playlist );
1826 /* Delegate method of NSWindow */
1827 /*- (void)windowWillClose:(NSNotification *)aNotification
1829 [o_btn_playlist setState: NSOffState];