1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2006 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create toggle buttons for the shuffle, repeat one, repeat all functions.
29 * reimplement enable/disable item
30 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
31 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
35 /*****************************************************************************
37 *****************************************************************************/
38 #include <stdlib.h> /* malloc(), free() */
39 #include <sys/param.h> /* for MAXPATHLEN */
42 #include <sys/mount.h>
48 #import "playlistinfo.h"
53 #import <vlc_interaction.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 [o_outline_view setTarget: self];
120 [o_outline_view setDelegate: self];
121 [o_outline_view setDataSource: self];
123 vlc_object_release( p_playlist );
129 [[o_tc_name headerCell] setStringValue:_NS("Name")];
130 [[o_tc_author headerCell] setStringValue:_NS("Author")];
131 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
134 - (NSOutlineView *)outlineView
136 return o_outline_view;
139 - (playlist_item_t *)selectedPlaylistItem
141 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
147 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
149 /* return the number of children for Obj-C pointer item */ /* DONE */
150 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
153 playlist_item_t *p_item = NULL;
154 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
156 if( p_playlist == NULL )
158 if( outlineView != o_outline_view )
160 vlc_object_release( p_playlist );
167 p_item = p_playlist->p_root_category;
171 p_item = (playlist_item_t *)[item pointerValue];
174 i_return = p_item->i_children;
175 vlc_object_release( p_playlist );
179 NSLog( @"%d children for %s", i_return, p_item->p_input->psz_name );
183 /* return the child at index for the Obj-C pointer item */ /* DONE */
184 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
186 playlist_item_t *p_return = NULL, *p_item = NULL;
188 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
191 if( p_playlist == NULL )
197 p_item = p_playlist->p_root_category;
201 p_item = (playlist_item_t *)[item pointerValue];
203 if( p_item && index < p_item->i_children && index >= 0 )
204 p_return = p_item->pp_children[index];
206 vlc_object_release( p_playlist );
208 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
209 NSLog( @"%s", p_return->p_input->psz_name);
212 o_value = [[NSValue valueWithPointer: p_return] retain];
213 NSLog( @"error missing value" );
218 /* is the item expandable */
219 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
222 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
224 if( p_playlist == NULL )
230 if( p_playlist->p_root_category )
232 i_return = p_playlist->p_root_category->i_children;
237 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
239 i_return = p_item->i_children;
241 vlc_object_release( p_playlist );
243 NSLog( @"expandable" );
250 /* retrieve the string values for the cells */
251 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
254 intf_thread_t *p_intf = VLCIntf;
255 playlist_t *p_playlist;
256 playlist_item_t *p_item;
258 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
260 /* Check to see if the playlist is present */
261 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
262 if( p_playlist == NULL ) return( @"error");
263 vlc_object_release( p_playlist);
265 p_item = (playlist_item_t *)[item pointerValue];
270 //NSLog( @"values for %p", p_item );
272 if( [[o_tc identifier] isEqualToString:@"1"] )
274 o_value = [NSString stringWithUTF8String:
275 p_item->p_input->psz_name];
276 if( o_value == NULL )
277 o_value = [NSString stringWithCString:
278 p_item->p_input->psz_name];
280 else if( [[o_tc identifier] isEqualToString:@"2"] && p_item->p_input->p_meta &&
281 p_item->p_input->p_meta->psz_artist && *p_item->p_input->p_meta->psz_artist )
283 o_value = [NSString stringWithUTF8String:
284 p_item->p_input->p_meta->psz_artist];
285 if( o_value == NULL )
286 o_value = [NSString stringWithCString:
287 p_item->p_input->p_meta->psz_artist];
289 else if( [[o_tc identifier] isEqualToString:@"3"] )
291 char psz_duration[MSTRTIME_MAX_SIZE];
292 mtime_t dur = p_item->p_input->i_duration;
295 secstotimestr( psz_duration, dur/1000000 );
296 o_value = [NSString stringWithUTF8String: psz_duration];
300 o_value = @"-:--:--";
309 /*****************************************************************************
310 * VLCPlaylistWizard implementation
311 *****************************************************************************/
312 @implementation VLCPlaylistWizard
314 - (IBAction)reloadOutlineView
316 /* Only reload the outlineview if the wizard window is open since this can
317 be quite long on big playlists */
318 if( [[o_outline_view window] isVisible] )
320 [o_outline_view reloadData];
326 /*****************************************************************************
327 * VLCPlaylist implementation
328 *****************************************************************************/
329 @implementation VLCPlaylist
336 o_nodes_array = [[NSMutableArray alloc] init];
337 o_items_array = [[NSMutableArray alloc] init];
344 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
346 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
351 [super awakeFromNib];
353 [o_outline_view setDoubleAction: @selector(playItem:)];
355 [o_outline_view registerForDraggedTypes:
356 [NSArray arrayWithObjects: NSFilenamesPboardType,
357 @"VLCPlaylistItemPboardType", nil]];
358 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
360 /* We need to check whether _defaultTableHeaderSortImage exists, since it
361 belongs to an Apple hidden private API, and then can "disapear" at any time*/
363 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
365 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
369 o_ascendingSortingImage = nil;
372 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
374 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
378 o_descendingSortingImage = nil;
381 o_tc_sortColumn = nil;
383 for( i_index = 0; i_index < p_list->i_count; i_index++ )
386 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
388 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
390 /* create the menu entries used in the playlist menu */
391 o_lmi = [[o_mi_services submenu] addItemWithTitle:
392 [NSString stringWithUTF8String:
393 p_parser->psz_longname ? p_parser->psz_longname :
394 ( p_parser->psz_shortname ? p_parser->psz_shortname:
395 p_parser->psz_object_name)]
396 action: @selector(servicesChange:)
398 [o_lmi setTarget: self];
399 [o_lmi setRepresentedObject:
400 [NSString stringWithCString: p_parser->psz_object_name]];
401 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
402 p_parser->psz_object_name ) )
403 [o_lmi setState: NSOnState];
405 /* create the menu entries for the main menu */
406 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
407 [NSString stringWithUTF8String:
408 p_parser->psz_longname ? p_parser->psz_longname :
409 ( p_parser->psz_shortname ? p_parser->psz_shortname:
410 p_parser->psz_object_name)]
411 action: @selector(servicesChange:)
413 [o_lmi setTarget: self];
414 [o_lmi setRepresentedObject:
415 [NSString stringWithCString: p_parser->psz_object_name]];
416 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
417 p_parser->psz_object_name ) )
418 [o_lmi setState: NSOnState];
421 vlc_list_release( p_list );
422 vlc_object_release( p_playlist );
424 //[self playlistUpdated];
427 - (void)searchfieldChanged:(NSNotification *)o_notification
429 [o_search_field setStringValue:[[o_notification object] stringValue]];
436 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
437 [o_mi_play setTitle: _NS("Play")];
438 [o_mi_delete setTitle: _NS("Delete")];
439 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
440 [o_mi_selectall setTitle: _NS("Select All")];
441 [o_mi_info setTitle: _NS("Information")];
442 [o_mi_preparse setTitle: _NS("Get Stream Information")];
443 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
444 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
445 [o_mi_services setTitle: _NS("Services discovery")];
446 [o_status_field setStringValue: [NSString stringWithFormat:
447 _NS("No items in the playlist")]];
449 [o_random_ckb setTitle: _NS("Random")];
451 [o_search_button setTitle: _NS("Search")];
453 [o_search_field setToolTip: _NS("Search in Playlist")];
454 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
455 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
456 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
457 [o_mi_addNode setTitle: _NS("Add Folder to Playlist")];
459 [o_save_accessory_text setStringValue: _NS("File Format:")];
460 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
461 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
464 - (void)playlistUpdated
468 /* Clear indications of any existing column sorting */
469 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
471 [o_outline_view setIndicatorImage:nil inTableColumn:
472 [[o_outline_view tableColumns] objectAtIndex:i]];
475 [o_outline_view setHighlightedTableColumn:nil];
476 o_tc_sortColumn = nil;
477 // TODO Find a way to keep the dict size to a minimum
478 //[o_outline_dict removeAllObjects];
479 [o_outline_view reloadData];
480 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
481 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
483 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
488 if( p_playlist->i_size >= 2 )
490 [o_status_field setStringValue: [NSString stringWithFormat:
491 _NS("%i items in the playlist"), p_playlist->i_size]];
495 if( p_playlist->i_size == 0 )
497 [o_status_field setStringValue: _NS("No items in the playlist")];
501 [o_status_field setStringValue: _NS("1 item in the playlist")];
504 vlc_object_release( p_playlist );
507 - (void)playModeUpdated
509 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
511 vlc_value_t val, val2;
513 if( p_playlist == NULL )
518 var_Get( p_playlist, "loop", &val2 );
519 var_Get( p_playlist, "repeat", &val );
520 if( val.b_bool == VLC_TRUE )
522 [o_loop_popup selectItemAtIndex: 1];
524 else if( val2.b_bool == VLC_TRUE )
526 [o_loop_popup selectItemAtIndex: 2];
530 [o_loop_popup selectItemAtIndex: 0];
533 var_Get( p_playlist, "random", &val );
534 [o_random_ckb setState: val.b_bool];
536 vlc_object_release( p_playlist );
539 - (void)updateRowSelection
544 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
546 playlist_item_t *p_item, *p_temp_item;
547 NSMutableArray *o_array = [NSMutableArray array];
549 if( p_playlist == NULL )
552 p_item = p_playlist->status.p_item;
555 vlc_object_release(p_playlist);
559 p_temp_item = p_item;
560 while( p_temp_item->p_parent )
562 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
563 p_temp_item = p_temp_item->p_parent;
564 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
566 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
568 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
574 for( j = 0; j < [o_array count] - 1; j++ )
577 if( ( o_item = [o_outline_dict objectForKey:
578 [NSString stringWithFormat: @"%p",
579 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
581 [o_outline_view expandItem: o_item];
586 i_row = [o_outline_view rowForItem:[o_outline_dict
587 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
589 [o_outline_view selectRow: i_row byExtendingSelection: NO];
590 [o_outline_view scrollRowToVisible: i_row];
592 vlc_object_release(p_playlist);
594 /* update our info-panel to reflect the new item */
595 [[[VLCMain sharedInstance] getInfo] updatePanel];
598 /* Check if p_item is a child of p_node recursively. We need to check the item
599 existence first since OSX sometimes tries to redraw items that have been
600 deleted. We don't do it when not required since this verification takes
601 quite a long time on big playlists (yes, pretty hacky). */
602 - (BOOL)isItem: (playlist_item_t *)p_item
603 inNode: (playlist_item_t *)p_node
604 checkItemExistence:(BOOL)b_check
607 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
609 playlist_item_t *p_temp_item = p_item;
611 if( p_playlist == NULL )
616 if( p_node == p_item )
618 vlc_object_release(p_playlist);
622 if( p_node->i_children < 1)
624 vlc_object_release(p_playlist);
631 vlc_mutex_lock( &p_playlist->object_lock );
635 /* Since outlineView: willDisplayCell:... may call this function with
636 p_items that don't exist anymore, first check if the item is still
637 in the playlist. Any cleaner solution welcomed. */
638 for( i = 0; i < p_playlist->i_all_size; i++ )
640 if( p_playlist->pp_all_items[i] == p_item ) break;
641 else if ( i == p_playlist->i_all_size - 1 )
643 vlc_object_release( p_playlist );
644 vlc_mutex_unlock( &p_playlist->object_lock );
652 p_temp_item = p_temp_item->p_parent;
653 if( p_temp_item == p_node )
655 vlc_mutex_unlock( &p_playlist->object_lock );
656 vlc_object_release( p_playlist );
660 vlc_mutex_unlock( &p_playlist->object_lock );
663 vlc_object_release( p_playlist );
667 /* This method is usefull for instance to remove the selected children of an
668 already selected node */
669 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
672 for( i = 0 ; i < [o_items count] ; i++ )
674 for ( j = 0 ; j < [o_nodes count] ; j++ )
676 if( o_items == o_nodes)
678 if( j == i ) continue;
680 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
681 inNode: [[o_nodes objectAtIndex:j] pointerValue]
682 checkItemExistence: NO] )
684 [o_items removeObjectAtIndex:i];
685 /* We need to execute the next iteration with the same index
686 since the current item has been deleted */
695 - (IBAction)savePlaylist:(id)sender
697 intf_thread_t * p_intf = VLCIntf;
698 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
701 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
702 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
704 //[o_save_panel setAllowedFileTypes: [NSArray arrayWithObjects: @"m3u", @"xpf", nil] ];
705 [o_save_panel setTitle: _NS("Save Playlist")];
706 [o_save_panel setPrompt: _NS("Save")];
707 [o_save_panel setAccessoryView: o_save_accessory_view];
709 if( [o_save_panel runModalForDirectory: nil
710 file: o_name] == NSOKButton )
712 NSString *o_filename = [o_save_panel filename];
714 if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
716 NSString * o_real_filename;
718 range.location = [o_filename length] - [@".xspf" length];
719 range.length = [@".xspf" length];
721 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
722 range: range] != NSOrderedSame )
724 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
728 o_real_filename = o_filename;
730 playlist_Export( p_playlist,
731 [o_real_filename fileSystemRepresentation],
732 p_playlist->p_local_category, "export-xspf" );
736 NSString * o_real_filename;
738 range.location = [o_filename length] - [@".m3u" length];
739 range.length = [@".m3u" length];
741 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
742 range: range] != NSOrderedSame )
744 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
748 o_real_filename = o_filename;
750 playlist_Export( p_playlist,
751 [o_real_filename fileSystemRepresentation],
752 p_playlist->p_local_category, "export-m3u" );
755 vlc_object_release( p_playlist );
758 /* When called retrieves the selected outlineview row and plays that node or item */
759 - (IBAction)playItem:(id)sender
761 intf_thread_t * p_intf = VLCIntf;
762 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
765 if( p_playlist != NULL )
767 playlist_item_t *p_item;
768 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 = p_item->p_parent;
782 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
784 p_item = p_node->pp_children[0];
791 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, p_node, p_item );
793 vlc_object_release( p_playlist );
797 /* When called retrieves the selected outlineview row and plays that node or item */
798 - (IBAction)preparseItem:(id)sender
801 NSMutableArray *o_to_preparse;
802 intf_thread_t * p_intf = VLCIntf;
803 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
806 o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
807 i_count = [o_to_preparse count];
809 if( p_playlist != NULL )
813 playlist_item_t *p_item = NULL;
815 for( i = 0; i < i_count; i++ )
817 o_number = [o_to_preparse lastObject];
818 i_row = [o_number intValue];
819 p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
820 [o_to_preparse removeObject: o_number];
821 [o_outline_view deselectRow: i_row];
825 if( p_item->i_children == -1 )
827 playlist_PreparseEnqueue( p_playlist, p_item->p_input );
831 msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
835 vlc_object_release( p_playlist );
837 [self playlistUpdated];
840 - (IBAction)servicesChange:(id)sender
842 NSMenuItem *o_mi = (NSMenuItem *)sender;
843 NSString *o_string = [o_mi representedObject];
844 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
846 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
847 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
849 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
851 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
852 [o_string cString] ) ? YES : NO];
854 vlc_object_release( p_playlist );
855 [self playlistUpdated];
859 - (IBAction)selectAll:(id)sender
861 [o_outline_view selectAll: nil];
864 - (IBAction)deleteItem:(id)sender
866 int i, i_count, i_row;
867 NSMutableArray *o_to_delete;
870 playlist_t * p_playlist;
871 intf_thread_t * p_intf = VLCIntf;
873 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
876 if ( p_playlist == NULL )
880 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
881 i_count = [o_to_delete count];
883 for( i = 0; i < i_count; i++ )
885 o_number = [o_to_delete lastObject];
886 i_row = [o_number intValue];
887 id o_item = [o_outline_view itemAtRow: i_row];
888 playlist_item_t *p_item = [o_item pointerValue];
889 [o_to_delete removeObject: o_number];
890 [o_outline_view deselectRow: i_row];
892 if( [[o_outline_view dataSource] outlineView:o_outline_view
893 numberOfChildrenOfItem: o_item] > 0 )
894 //is a node and not an item
896 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
897 [self isItem: p_playlist->status.p_item inNode:
898 ((playlist_item_t *)[o_item pointerValue])
899 checkItemExistence: NO] == YES )
901 // if current item is in selected node and is playing then stop playlist
902 playlist_Stop( p_playlist );
904 vlc_mutex_lock( &p_playlist->object_lock );
905 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
906 vlc_mutex_unlock( &p_playlist->object_lock );
910 playlist_LockDelete( p_playlist, p_item->i_id );
913 [self playlistUpdated];
914 vlc_object_release( p_playlist );
917 - (IBAction)sortNodeByName:(id)sender
919 [self sortNode: SORT_TITLE];
922 - (IBAction)sortNodeByAuthor:(id)sender
924 [self sortNode: SORT_AUTHOR];
927 - (void)sortNode:(int)i_mode
929 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
931 playlist_item_t * p_item;
933 if (p_playlist == NULL)
938 if( [o_outline_view selectedRow] > -1 )
940 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
944 /*If no item is selected, sort the whole playlist*/
946 p_item = p_playlist->p_root_category;
949 if( p_item->i_children > -1 ) // the item is a node
951 vlc_mutex_lock( &p_playlist->object_lock );
952 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
953 vlc_mutex_unlock( &p_playlist->object_lock );
957 vlc_mutex_lock( &p_playlist->object_lock );
958 playlist_RecursiveNodeSort( p_playlist,
959 p_item->p_parent, i_mode, ORDER_NORMAL );
960 vlc_mutex_unlock( &p_playlist->object_lock );
962 vlc_object_release( p_playlist );
963 [self playlistUpdated];
966 - (input_item_t *)createItem:(NSDictionary *)o_one_item
968 intf_thread_t * p_intf = VLCIntf;
969 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
972 if( p_playlist == NULL )
976 input_item_t *p_input;
978 BOOL b_rem = FALSE, b_dir = FALSE;
979 NSString *o_uri, *o_name;
984 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
985 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
986 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
988 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
989 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
991 int i_count, i_index;
992 struct statfs *mounts = NULL;
994 i_count = getmntinfo (&mounts, MNT_NOWAIT);
995 /* getmntinfo returns a pointer to static data. Do not free. */
996 for( i_index = 0 ; i_index < i_count; i_index++ )
998 NSMutableString *o_temp, *o_temp2;
999 o_temp = [NSMutableString stringWithString: o_uri];
1000 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
1001 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1002 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1003 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
1005 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1007 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1011 /* If no name, then make a guess */
1012 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1014 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1015 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1016 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1018 /* All of this is to make sure CD's play when you D&D them on VLC */
1019 /* Converts mountpoint to a /dev file */
1022 NSMutableString *o_temp;
1024 buf = (struct statfs *) malloc (sizeof(struct statfs));
1025 statfs( [o_uri fileSystemRepresentation], buf );
1026 psz_dev = strdup(buf->f_mntfromname);
1027 o_temp = [NSMutableString stringWithCString: psz_dev ];
1028 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1029 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1030 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1034 p_input = input_ItemNew( p_playlist, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1040 for( i = 0; i < (int)[o_options count]; i++ )
1042 vlc_input_item_AddOption( p_input, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1046 /* Recent documents menu */
1047 o_true_file = [NSURL fileURLWithPath: o_uri];
1048 if( o_true_file != nil )
1050 [[NSDocumentController sharedDocumentController]
1051 noteNewRecentDocumentURL: o_true_file];
1054 vlc_object_release( p_playlist );
1058 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1061 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1063 if( p_playlist == NULL )
1068 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1070 input_item_t *p_input;
1071 NSDictionary *o_one_item;
1074 o_one_item = [o_array objectAtIndex: i_item];
1075 p_input = [self createItem: o_one_item];
1082 playlist_PlaylistAddInput( p_playlist, p_input, PLAYLIST_INSERT,
1083 i_position == -1 ? PLAYLIST_END : i_position + i_item );
1085 if( i_item == 0 && !b_enqueue )
1087 playlist_item_t *p_item;
1088 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1089 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, NULL, p_item );
1093 playlist_item_t *p_item;
1094 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1095 playlist_Control( p_playlist, PLAYLIST_PREPARSE, p_item );
1098 [self playlistUpdated];
1099 vlc_object_release( p_playlist );
1102 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position enqueue:(BOOL)b_enqueue
1105 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1107 if( p_playlist == NULL )
1112 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1114 input_item_t *p_input;
1115 NSDictionary *o_one_item;
1118 o_one_item = [o_array objectAtIndex: i_item];
1119 p_input = [self createItem: o_one_item];
1126 playlist_NodeAddInput( p_playlist, p_input, p_node,
1129 PLAYLIST_END : i_position + i_item );
1132 if( i_item == 0 && !b_enqueue )
1134 playlist_item_t *p_item;
1135 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1136 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, NULL, p_item );
1140 playlist_item_t *p_item;
1141 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1142 playlist_Control( p_playlist, PLAYLIST_PREPARSE, p_item );
1145 [self playlistUpdated];
1146 vlc_object_release( p_playlist );
1149 - (IBAction)handlePopUp:(id)sender
1152 intf_thread_t * p_intf = VLCIntf;
1153 vlc_value_t val1,val2;
1154 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1156 if( p_playlist == NULL )
1161 switch( [o_loop_popup indexOfSelectedItem] )
1166 var_Set( p_playlist, "loop", val1 );
1168 var_Set( p_playlist, "repeat", val1 );
1169 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1174 var_Set( p_playlist, "repeat", val1 );
1176 var_Set( p_playlist, "loop", val1 );
1177 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1181 var_Get( p_playlist, "repeat", &val1 );
1182 var_Get( p_playlist, "loop", &val2 );
1183 if( val1.b_bool || val2.b_bool )
1186 var_Set( p_playlist, "repeat", val1 );
1187 var_Set( p_playlist, "loop", val1 );
1188 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1192 vlc_object_release( p_playlist );
1193 [self playlistUpdated];
1196 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1198 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1200 playlist_item_t *p_selected_item;
1201 int i_current, i_selected_row;
1206 i_selected_row = [o_outline_view selectedRow];
1207 if (i_selected_row < 0)
1210 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1211 i_selected_row] pointerValue];
1213 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1216 NSString *o_current_name, *o_current_author;
1218 vlc_mutex_lock( &p_playlist->object_lock );
1219 o_current_name = [NSString stringWithUTF8String:
1220 p_item->pp_children[i_current]->p_input->psz_name];
1221 psz_temp = vlc_input_item_GetInfo( p_item->p_input ,
1222 _("Meta-information"),_("Artist") );
1223 o_current_author = [NSString stringWithUTF8String: psz_temp];
1225 vlc_mutex_unlock( &p_playlist->object_lock );
1227 if( p_selected_item == p_item->pp_children[i_current] &&
1228 b_selected_item_met == NO )
1230 b_selected_item_met = YES;
1232 else if( p_selected_item == p_item->pp_children[i_current] &&
1233 b_selected_item_met == YES )
1235 vlc_object_release( p_playlist );
1238 else if( b_selected_item_met == YES &&
1239 ( [o_current_name rangeOfString:[o_search_field
1240 stringValue] options:NSCaseInsensitiveSearch ].length ||
1241 [o_current_author rangeOfString:[o_search_field
1242 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1244 vlc_object_release( p_playlist );
1245 /*Adds the parent items in the result array as well, so that we can
1247 return [NSMutableArray arrayWithObject: [NSValue
1248 valueWithPointer: p_item->pp_children[i_current]]];
1250 if( p_item->pp_children[i_current]->i_children > 0 )
1252 id o_result = [self subSearchItem:
1253 p_item->pp_children[i_current]];
1254 if( o_result != NULL )
1256 vlc_object_release( p_playlist );
1257 [o_result insertObject: [NSValue valueWithPointer:
1258 p_item->pp_children[i_current]] atIndex:0];
1263 vlc_object_release( p_playlist );
1267 - (IBAction)searchItem:(id)sender
1269 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1276 b_selected_item_met = NO;
1278 if( p_playlist == NULL )
1281 /*First, only search after the selected item:*
1282 *(b_selected_item_met = NO) */
1283 o_result = [self subSearchItem:p_playlist->p_root_category];
1284 if( o_result == NULL )
1286 /* If the first search failed, search again from the beginning */
1287 o_result = [self subSearchItem:p_playlist->p_root_category];
1289 if( o_result != NULL )
1292 if( [[o_result objectAtIndex: 0] pointerValue] ==
1293 p_playlist->p_local_category )
1298 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1300 [o_outline_view expandItem: [o_outline_dict objectForKey:
1301 [NSString stringWithFormat: @"%p",
1302 [[o_result objectAtIndex: i] pointerValue]]]];
1304 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1305 [NSString stringWithFormat: @"%p",
1306 [[o_result objectAtIndex: [o_result count] - 1 ]
1311 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1312 [o_outline_view scrollRowToVisible: i_row];
1314 vlc_object_release( p_playlist );
1317 - (IBAction)recursiveExpandNode:(id)sender
1319 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1320 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1322 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1323 isItemExpandable: o_item] )
1325 o_item = [o_outline_dict objectForKey: [NSString
1326 stringWithFormat: @"%p", p_item->p_parent]];
1329 /* We need to collapse the node first, since OSX refuses to recursively
1330 expand an already expanded node, even if children nodes are collapsed. */
1331 [o_outline_view collapseItem: o_item collapseChildren: YES];
1332 [o_outline_view expandItem: o_item expandChildren: YES];
1335 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1339 vlc_bool_t b_item_sel;
1341 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1343 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1344 [o_outline_view selectedRow] != -1 );
1345 b_rows = [o_outline_view numberOfRows] != 0;
1347 [o_mi_play setEnabled: b_item_sel];
1348 [o_mi_delete setEnabled: b_item_sel];
1349 [o_mi_selectall setEnabled: b_rows];
1350 [o_mi_info setEnabled: b_item_sel];
1351 [o_mi_preparse setEnabled: b_item_sel];
1352 [o_mi_recursive_expand setEnabled: b_item_sel];
1353 [o_mi_sort_name setEnabled: b_item_sel];
1354 [o_mi_sort_author setEnabled: b_item_sel];
1356 return( o_ctx_menu );
1359 - (void)outlineView: (NSTableView*)o_tv
1360 didClickTableColumn:(NSTableColumn *)o_tc
1362 int i_mode = 0, i_type;
1363 intf_thread_t *p_intf = VLCIntf;
1365 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1367 if( p_playlist == NULL )
1372 /* Check whether the selected table column header corresponds to a
1373 sortable table column*/
1374 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1376 vlc_object_release( p_playlist );
1380 if( o_tc_sortColumn == o_tc )
1382 b_isSortDescending = !b_isSortDescending;
1386 b_isSortDescending = VLC_FALSE;
1389 if( o_tc == o_tc_name )
1391 i_mode = SORT_TITLE;
1393 else if( o_tc == o_tc_author )
1395 i_mode = SORT_AUTHOR;
1398 if( b_isSortDescending )
1400 i_type = ORDER_REVERSE;
1404 i_type = ORDER_NORMAL;
1407 vlc_mutex_lock( &p_playlist->object_lock );
1408 playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_category, i_mode, i_type );
1409 vlc_mutex_unlock( &p_playlist->object_lock );
1411 vlc_object_release( p_playlist );
1412 [self playlistUpdated];
1414 o_tc_sortColumn = o_tc;
1415 [o_outline_view setHighlightedTableColumn:o_tc];
1417 if( b_isSortDescending )
1419 [o_outline_view setIndicatorImage:o_descendingSortingImage
1420 inTableColumn:o_tc];
1424 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1425 inTableColumn:o_tc];
1430 - (void)outlineView:(NSOutlineView *)outlineView
1431 willDisplayCell:(id)cell
1432 forTableColumn:(NSTableColumn *)tableColumn
1435 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1440 if( !p_playlist ) return;
1442 o_playing_item = [o_outline_dict objectForKey:
1443 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1445 if( [self isItem: [o_playing_item pointerValue] inNode:
1446 [item pointerValue] checkItemExistence: YES]
1447 || [o_playing_item isEqual: item] )
1449 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1453 [cell setFont: [NSFont systemFontOfSize: 0]];
1455 vlc_object_release( p_playlist );
1458 - (IBAction)addNode:(id)sender
1460 /* we have to create a new thread here because otherwise we would block the
1461 * interface since the interaction-stuff and this code would run in the same
1463 [NSThread detachNewThreadSelector: @selector(addNodeThreadedly)
1464 toTarget: self withObject:nil];
1465 [self playlistUpdated];
1468 - (void)addNodeThreadedly
1470 NSAutoreleasePool * ourPool = [[NSAutoreleasePool alloc] init];
1472 /* simply adds a new node to the end of the playlist */
1473 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1475 vlc_thread_set_priority( p_playlist, VLC_THREAD_PRIORITY_LOW );
1483 char *psz_name = NULL;
1484 playlist_item_t * p_item;
1485 ret_v = intf_UserStringInput( p_playlist, _("New Node"),
1486 _("Please enter a name for the new node."), &psz_name );
1487 if( psz_name != NULL && psz_name != "" )
1488 p_item = playlist_NodeCreate( p_playlist, psz_name,
1489 p_playlist->p_local_category );
1491 p_item = playlist_NodeCreate( p_playlist, _("Empty Folder"),
1492 p_playlist->p_local_category );
1495 msg_Warn( VLCIntf, "node creation failed" );
1497 vlc_object_release( p_playlist );
1503 @implementation VLCPlaylist (NSOutlineViewDataSource)
1505 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1507 id o_value = [super outlineView: outlineView child: index ofItem: item];
1508 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1511 if( !p_playlist ) return nil;
1513 if( p_playlist->i_size >= 2 )
1515 [o_status_field setStringValue: [NSString stringWithFormat:
1516 _NS("%i items in the playlist"), p_playlist->i_size]];
1520 if( p_playlist->i_size == 0 )
1522 [o_status_field setStringValue: _NS("No items in the playlist")];
1526 [o_status_field setStringValue: _NS("1 item in the playlist")];
1529 vlc_object_release( p_playlist );
1531 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1532 [o_value pointerValue]]];
1533 NSLog( @"add item %p", [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_local_category 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 if item is not a node */
1598 if( index == NSOutlineViewDropOnItemIndex &&
1599 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1601 vlc_object_release( p_playlist );
1602 return NSDragOperationNone;
1606 /* We refuse to drop an item in anything else than a child of the General
1607 Node. We still accept items that would be root nodes of the outlineview
1608 however, to allow drop in an empty playlist. */
1609 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_local_category
1610 checkItemExistence: NO] || item == nil) )
1612 vlc_object_release( p_playlist );
1613 return NSDragOperationNone;
1616 /* Drop from the Playlist */
1617 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1620 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1622 /* We refuse to Drop in a child of an item we are moving */
1623 if( [self isItem: [item pointerValue] inNode:
1624 [[o_nodes_array objectAtIndex: i] pointerValue]
1625 checkItemExistence: NO] )
1627 vlc_object_release( p_playlist );
1628 return NSDragOperationNone;
1631 vlc_object_release(p_playlist);
1632 return NSDragOperationMove;
1635 /* Drop from the Finder */
1636 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1638 vlc_object_release(p_playlist);
1639 return NSDragOperationGeneric;
1641 vlc_object_release(p_playlist);
1642 return NSDragOperationNone;
1645 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1647 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1649 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1651 if( !p_playlist ) return NO;
1653 /* Drag & Drop inside the playlist */
1654 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1656 int i_row, i_removed_from_node = 0;
1658 playlist_item_t *p_new_parent, *p_item = NULL;
1659 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1661 /* If the item is to be dropped as root item of the outline, make it a
1662 child of the General node.
1663 Else, choose the proposed parent as parent. */
1664 if( item == nil ) p_new_parent = p_playlist->p_local_category;
1665 else p_new_parent = [item pointerValue];
1667 /* Make sure the proposed parent is a node.
1668 (This should never be true) */
1669 if( p_new_parent->i_children < 0 )
1671 vlc_object_release( p_playlist );
1675 for( i = 0; i < [o_all_items count]; i++ )
1677 playlist_item_t *p_old_parent = NULL;
1678 int i_old_index = 0;
1680 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1681 p_old_parent = p_item->p_parent;
1684 /* We may need the old index later */
1685 if( p_new_parent == p_old_parent )
1688 for( j = 0; j < p_old_parent->i_children; j++ )
1690 if( p_old_parent->pp_children[j] == p_item )
1698 vlc_mutex_lock( &p_playlist->object_lock );
1699 // Acually detach the item from the old position
1700 if( playlist_NodeRemoveItem( 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 */
1711 if ((p_new_parent == p_old_parent &&
1712 i_old_index < index + (int)i) )
1714 i_removed_from_node++;
1716 i_new_index = index + i - i_removed_from_node;
1718 // Reattach the item to the new position
1719 playlist_NodeInsert( p_playlist, p_item, p_new_parent, i_new_index );
1721 vlc_mutex_unlock( &p_playlist->object_lock );
1723 [self playlistUpdated];
1724 i_row = [o_outline_view rowForItem:[o_outline_dict
1725 objectForKey:[NSString stringWithFormat: @"%p",
1726 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1730 i_row = [o_outline_view rowForItem:[o_outline_dict
1731 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1734 [o_outline_view deselectAll: self];
1735 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1736 [o_outline_view scrollRowToVisible: i_row];
1738 vlc_object_release(p_playlist);
1742 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1745 playlist_item_t *p_node = [item pointerValue];
1747 NSArray *o_array = [NSArray array];
1748 NSArray *o_values = [[o_pasteboard propertyListForType:
1749 NSFilenamesPboardType]
1750 sortedArrayUsingSelector:
1751 @selector(caseInsensitiveCompare:)];
1753 for( i = 0; i < (int)[o_values count]; i++)
1755 NSDictionary *o_dic;
1756 o_dic = [NSDictionary dictionaryWithObject:[o_values
1757 objectAtIndex:i] forKey:@"ITEM_URL"];
1758 o_array = [o_array arrayByAddingObject: o_dic];
1763 [self appendArray: o_array atPos: index enqueue: YES];
1765 /* This should never occur */
1766 else if( p_node->i_children == -1 )
1768 vlc_object_release( p_playlist );
1773 [self appendNodeArray: o_array inNode: p_node
1774 atPos: index enqueue: YES];
1776 vlc_object_release( p_playlist );
1779 vlc_object_release( p_playlist );
1783 /* Delegate method of NSWindow */
1784 /*- (void)windowWillClose:(NSNotification *)aNotification
1786 [o_btn_playlist setState: NSOffState];