1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2012 VLC authors and VideoLAN
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>
10 * Felix Paul Kühne <fkuehne at videolan dot org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
28 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
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>
47 #import "playlistinfo.h"
53 #import <vlc_services_discovery.h>
55 #import <vlc_interface.h>
60 /*****************************************************************************
61 * VLCPlaylistView implementation
62 *****************************************************************************/
63 @implementation VLCPlaylistView
65 - (NSMenu *)menuForEvent:(NSEvent *)o_event
67 return( [[self delegate] menuForEvent: o_event] );
70 - (void)keyDown:(NSEvent *)o_event
74 if( [[o_event characters] length] )
76 key = [[o_event characters] characterAtIndex: 0];
81 case NSDeleteCharacter:
82 case NSDeleteFunctionKey:
83 case NSDeleteCharFunctionKey:
84 case NSBackspaceCharacter:
85 [[self delegate] deleteItem:self];
88 case NSEnterCharacter:
89 case NSCarriageReturnCharacter:
90 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] playItem:self];
94 [super keyDown: o_event];
101 /*****************************************************************************
102 * VLCPlaylistCommon implementation
104 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
105 * It contains the common methods and elements of these 2 entities.
106 *****************************************************************************/
107 @implementation VLCPlaylistCommon
111 playlist_t * p_playlist = pl_Get( VLCIntf );
112 p_current_root_item = p_playlist->p_local_category;
117 o_outline_dict = [[NSMutableDictionary alloc] init];
124 playlist_t * p_playlist = pl_Get( VLCIntf );
125 [o_outline_view setTarget: self];
126 [o_outline_view setDelegate: self];
127 [o_outline_view setDataSource: self];
128 [o_outline_view setAllowsEmptySelection: NO];
129 [o_outline_view expandItem: [o_outline_view itemAtRow:0]];
131 [o_outline_view_other setTarget: self];
132 [o_outline_view_other setDelegate: self];
133 [o_outline_view_other setDataSource: self];
134 [o_outline_view_other setAllowsEmptySelection: NO];
141 [[o_tc_name headerCell] setStringValue:_NS("Name")];
142 [[o_tc_author headerCell] setStringValue:_NS("Author")];
143 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
145 [[o_tc_name_other headerCell] setStringValue:_NS("Name")];
146 [[o_tc_author_other headerCell] setStringValue:_NS("Author")];
147 [[o_tc_duration_other headerCell] setStringValue:_NS("Duration")];
150 - (void)setPlaylistRoot: (playlist_item_t *)root_item
152 p_current_root_item = root_item;
153 [o_outline_view reloadData];
154 [o_outline_view_other reloadData];
157 - (playlist_item_t *)currentPlaylistRoot
159 return p_current_root_item;
162 - (void)swapPlaylists:(id)newList
164 if(newList != o_outline_view)
166 id o_outline_view_temp = o_outline_view;
167 id o_tc_author_temp = o_tc_author;
168 id o_tc_duration_temp = o_tc_duration;
169 id o_tc_name_temp = o_tc_name;
170 o_outline_view = o_outline_view_other;
171 o_tc_author = o_tc_author_other;
172 o_tc_duration = o_tc_duration_other;
173 o_tc_name = o_tc_name_other;
174 o_outline_view_other = o_outline_view_temp;
175 o_tc_author_other = o_tc_author_temp;
176 o_tc_duration_other = o_tc_duration_temp;
177 o_tc_name_other = o_tc_name_temp;
181 - (NSOutlineView *)outlineView
183 return o_outline_view;
186 - (playlist_item_t *)selectedPlaylistItem
188 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
194 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
195 /* return the number of children for Obj-C pointer item */ /* DONE */
196 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
199 playlist_item_t *p_item = NULL;
200 playlist_t * p_playlist = pl_Get( VLCIntf );
201 //assert( outlineView == o_outline_view );
206 p_item = p_current_root_item;
209 p_item = (playlist_item_t *)[item pointerValue];
212 i_return = p_item->i_children;
215 return i_return > 0 ? i_return : 0;
218 /* return the child at index for the Obj-C pointer item */ /* DONE */
219 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
221 playlist_item_t *p_return = NULL, *p_item = NULL;
223 playlist_t * p_playlist = pl_Get( VLCIntf );
229 p_item = p_current_root_item;
233 p_item = (playlist_item_t *)[item pointerValue];
235 if( p_item && index < p_item->i_children && index >= 0 )
236 p_return = p_item->pp_children[index];
239 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
243 /* FIXME: Why is there a warning if that happens all the time and seems
244 * to be normal? Add an assert and fix it.
245 * msg_Warn( VLCIntf, "playlist item misses pointer value, adding one" ); */
246 o_value = [[NSValue valueWithPointer: p_return] retain];
251 /* is the item expandable */
252 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
255 playlist_t *p_playlist = pl_Get( VLCIntf );
261 if( p_current_root_item )
263 i_return = p_current_root_item->i_children;
268 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
270 i_return = p_item->i_children;
274 return (i_return >= 0);
277 /* retrieve the string values for the cells */
278 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
281 playlist_item_t *p_item;
283 /* For error handling */
284 static BOOL attempted_reload = NO;
286 if( item == nil || ![item isKindOfClass: [NSValue class]] )
288 /* Attempt to fix the error by asking for a data redisplay
289 * This might cause infinite loop, so add a small check */
290 if( !attempted_reload )
292 attempted_reload = YES;
293 [outlineView reloadData];
298 p_item = (playlist_item_t *)[item pointerValue];
299 if( !p_item || !p_item->p_input )
301 /* Attempt to fix the error by asking for a data redisplay
302 * This might cause infinite loop, so add a small check */
303 if( !attempted_reload )
305 attempted_reload = YES;
306 [outlineView reloadData];
311 attempted_reload = NO;
313 if( [[o_tc identifier] isEqualToString:@"name"] )
315 /* sanity check to prevent the NSString class from crashing */
316 char *psz_title = input_item_GetTitleFbName( p_item->p_input );
319 o_value = [NSString stringWithUTF8String: psz_title];
323 else if( [[o_tc identifier] isEqualToString:@"artist"] )
325 char *psz_artist = input_item_GetArtist( p_item->p_input );
327 o_value = [NSString stringWithUTF8String: psz_artist];
330 else if( [[o_tc identifier] isEqualToString:@"duration"] )
332 char psz_duration[MSTRTIME_MAX_SIZE];
333 mtime_t dur = input_item_GetDuration( p_item->p_input );
336 secstotimestr( psz_duration, dur/1000000 );
337 o_value = [NSString stringWithUTF8String: psz_duration];
342 else if( [[o_tc identifier] isEqualToString:@"status"] )
344 if( input_item_HasErrorWhenReading( p_item->p_input ) )
346 o_value = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kAlertCautionIcon)];
347 [o_value setSize: NSMakeSize(16,16)];
355 /*****************************************************************************
356 * VLCPlaylistWizard implementation
357 *****************************************************************************/
358 @implementation VLCPlaylistWizard
360 - (IBAction)reloadOutlineView
362 /* Only reload the outlineview if the wizard window is open since this can
363 be quite long on big playlists */
364 if( [[o_outline_view window] isVisible] )
366 [o_outline_view reloadData];
372 /*****************************************************************************
373 * extension to NSOutlineView's interface to fix compilation warnings
374 * and let us access these 2 functions properly
375 * this uses a private Apple-API, but works fine on all current OSX releases
376 * keep checking for compatiblity with future releases though
377 *****************************************************************************/
379 @interface NSOutlineView (UndocumentedSortImages)
380 + (NSImage *)_defaultTableHeaderSortImage;
381 + (NSImage *)_defaultTableHeaderReverseSortImage;
385 /*****************************************************************************
386 * VLCPlaylist implementation
387 *****************************************************************************/
388 @implementation VLCPlaylist
395 o_nodes_array = [[NSMutableArray alloc] init];
396 o_items_array = [[NSMutableArray alloc] init];
403 [o_nodes_array release];
404 [o_items_array release];
410 playlist_t * p_playlist = pl_Get( VLCIntf );
414 [super awakeFromNib];
416 [o_outline_view setDoubleAction: @selector(playItem:)];
417 [o_outline_view_other setDoubleAction: @selector(playItem:)];
419 [o_outline_view registerForDraggedTypes:
420 [NSArray arrayWithObjects: NSFilenamesPboardType,
421 @"VLCPlaylistItemPboardType", nil]];
422 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
424 [o_outline_view_other registerForDraggedTypes:
425 [NSArray arrayWithObjects: NSFilenamesPboardType,
426 @"VLCPlaylistItemPboardType", nil]];
427 [o_outline_view_other setIntercellSpacing: NSMakeSize (0.0, 1.0)];
429 /* This uses private Apple API which works fine until 10.5.
430 * We need to keep checking in the future!
431 * These methods are being added artificially to NSOutlineView's interface above */
432 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
433 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
435 o_tc_sortColumn = nil;
438 char ** ppsz_services = vlc_sd_GetNames( VLCIntf, &ppsz_name, NULL );
442 for( i = 0; ppsz_services[i]; i++ )
447 char * name = ppsz_name[i] ? ppsz_name[i] : ppsz_services[i];
448 /* Check whether to enable these menuitems */
449 b_enabled = playlist_IsServicesDiscoveryLoaded( p_playlist, ppsz_services[i] );
451 /* Create the menu entries used in the playlist menu */
452 o_lmi = [[o_mi_services submenu] addItemWithTitle:
453 [NSString stringWithUTF8String: name]
454 action: @selector(servicesChange:)
456 [o_lmi setTarget: self];
457 [o_lmi setRepresentedObject: [NSString stringWithUTF8String: ppsz_services[i]]];
458 if( b_enabled ) [o_lmi setState: NSOnState];
460 /* Create the menu entries for the main menu */
461 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
462 [NSString stringWithUTF8String: name]
463 action: @selector(servicesChange:)
465 [o_lmi setTarget: self];
466 [o_lmi setRepresentedObject: [NSString stringWithUTF8String: ppsz_services[i]]];
467 if( b_enabled ) [o_lmi setState: NSOnState];
469 free( ppsz_services[i] );
470 free( ppsz_name[i] );
472 free( ppsz_services );
477 - (void)searchfieldChanged:(NSNotification *)o_notification
479 [o_search_field setStringValue:[[o_notification object] stringValue]];
486 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
487 [o_mi_play setTitle: _NS("Play")];
488 [o_mi_delete setTitle: _NS("Delete")];
489 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
490 [o_mi_selectall setTitle: _NS("Select All")];
491 [o_mi_info setTitle: _NS("Media Information...")];
492 [o_mi_dl_cover_art setTitle: _NS("Download Cover Art")];
493 [o_mi_preparse setTitle: _NS("Fetch Meta Data")];
494 [o_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
495 [o_mm_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
496 [[o_mm_mi_revealInFinder menu] setAutoenablesItems: NO];
497 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
498 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
499 [o_mi_services setTitle: _NS("Services discovery")];
500 [o_mm_mi_services setTitle: _NS("Services discovery")];
502 [o_search_field setToolTip: _NS("Search in Playlist")];
503 [o_search_field_other setToolTip: _NS("Search in Playlist")];
505 [o_save_accessory_text setStringValue: _NS("File Format:")];
506 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
507 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
508 [[o_save_accessory_popup itemAtIndex:2] setTitle: _NS("HTML Playlist")];
511 - (void)swapPlaylists:(id)newList
513 if(newList != o_outline_view)
515 id o_search_field_temp = o_search_field;
516 o_search_field = o_search_field_other;
517 o_search_field_other = o_search_field_temp;
518 [super swapPlaylists:newList];
519 [self playlistUpdated];
523 - (void)playlistUpdated
525 /* Clear indications of any existing column sorting */
526 NSUInteger count = [[o_outline_view tableColumns] count];
527 for( NSUInteger i = 0 ; i < 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] wizard] playlistWizard] reloadOutlineView];
539 [[[[VLCMain sharedInstance] bookmarks] dataTable] reloadData];
541 [self outlineViewSelectionDidChange: nil];
542 [[VLCMain sharedInstance] updateMainWindow];
545 - (void)outlineViewSelectionDidChange:(NSNotification *)notification
548 playlist_item_t * p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
552 /* update the state of our Reveal-in-Finder menu items */
553 NSMutableString *o_mrl;
554 char *psz_uri = input_item_GetURI( p_item->p_input );
556 [o_mi_revealInFinder setEnabled: NO];
557 [o_mm_mi_revealInFinder setEnabled: NO];
560 o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
562 /* perform some checks whether it is a file and if it is local at all... */
563 NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
564 if( prefix_range.location != NSNotFound )
565 [o_mrl deleteCharactersInRange: prefix_range];
567 if( [o_mrl characterAtIndex:0] == '/' )
569 [o_mi_revealInFinder setEnabled: YES];
570 [o_mm_mi_revealInFinder setEnabled: YES];
575 /* update our info-panel to reflect the new item */
576 [[[VLCMain sharedInstance] info] updatePanelWithItem:p_item->p_input];
580 - (BOOL)isSelectionEmpty
582 return [o_outline_view selectedRow] == -1;
585 - (void)updateRowSelection
588 playlist_t *p_playlist = pl_Get( VLCIntf );
589 playlist_item_t *p_item, *p_temp_item;
590 NSMutableArray *o_array = [NSMutableArray array];
593 p_item = playlist_CurrentPlayingItem( p_playlist );
600 p_temp_item = p_item;
601 while( p_temp_item->p_parent )
603 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
604 p_temp_item = p_temp_item->p_parent;
608 NSUInteger count = [o_array count];
609 for( NSUInteger j = 0; j < count - 1; j++ )
612 if( ( o_item = [o_outline_dict objectForKey:
613 [NSString stringWithFormat: @"%p",
614 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
616 [o_outline_view expandItem: o_item];
620 id o_item = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_item]];
621 NSInteger i_index = [o_outline_view rowForItem:o_item];
622 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_index] byExtendingSelection:NO];
625 /* Check if p_item is a child of p_node recursively. We need to check the item
626 existence first since OSX sometimes tries to redraw items that have been
627 deleted. We don't do it when not required since this verification takes
628 quite a long time on big playlists (yes, pretty hacky). */
630 - (BOOL)isItem: (playlist_item_t *)p_item
631 inNode: (playlist_item_t *)p_node
632 checkItemExistence:(BOOL)b_check
633 locked:(BOOL)b_locked
636 playlist_t * p_playlist = pl_Get( VLCIntf );
637 playlist_item_t *p_temp_item = p_item;
639 if( p_node == p_item )
642 if( p_node->i_children < 1)
648 if(!b_locked) PL_LOCK;
652 /* Since outlineView: willDisplayCell:... may call this function with
653 p_items that don't exist anymore, first check if the item is still
654 in the playlist. Any cleaner solution welcomed. */
655 for( i = 0; i < p_playlist->all_items.i_size; i++ )
657 if( ARRAY_VAL( p_playlist->all_items, i) == p_item ) break;
658 else if ( i == p_playlist->all_items.i_size - 1 )
660 if(!b_locked) PL_UNLOCK;
668 p_temp_item = p_temp_item->p_parent;
669 if( p_temp_item == p_node )
671 if(!b_locked) PL_UNLOCK;
675 if(!b_locked) PL_UNLOCK;
680 - (BOOL)isItem: (playlist_item_t *)p_item
681 inNode: (playlist_item_t *)p_node
682 checkItemExistence:(BOOL)b_check
684 return [self isItem:p_item inNode:p_node checkItemExistence:b_check locked:NO];
687 /* This method is useful for instance to remove the selected children of an
688 already selected node */
689 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
691 NSUInteger itemCount = [o_items count];
692 NSUInteger nodeCount = [o_nodes count];
693 for( NSUInteger i = 0 ; i < itemCount ; i++ )
695 for ( NSUInteger j = 0 ; j < nodeCount ; j++ )
697 if( o_items == o_nodes)
699 if( j == i ) continue;
701 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
702 inNode: [[o_nodes objectAtIndex:j] pointerValue]
703 checkItemExistence: NO locked:NO] )
705 [o_items removeObjectAtIndex:i];
706 /* We need to execute the next iteration with the same index
707 since the current item has been deleted */
715 - (IBAction)savePlaylist:(id)sender
717 playlist_t * p_playlist = pl_Get( VLCIntf );
719 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
720 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
722 [o_save_panel setTitle: _NS("Save Playlist")];
723 [o_save_panel setPrompt: _NS("Save")];
724 [o_save_panel setAccessoryView: o_save_accessory_view];
726 if( [o_save_panel runModalForDirectory: nil
727 file: o_name] == NSOKButton )
729 NSString *o_filename = [[o_save_panel URL] path];
731 if( [o_save_accessory_popup indexOfSelectedItem] == 0 )
733 NSString * o_real_filename;
735 range.location = [o_filename length] - [@".m3u" length];
736 range.length = [@".m3u" length];
738 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
739 range: range] != NSOrderedSame )
741 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
745 o_real_filename = o_filename;
747 playlist_Export( p_playlist,
748 [o_real_filename fileSystemRepresentation],
749 p_playlist->p_local_category, "export-m3u" );
751 else if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
753 NSString * o_real_filename;
755 range.location = [o_filename length] - [@".xspf" length];
756 range.length = [@".xspf" length];
758 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
759 range: range] != NSOrderedSame )
761 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
765 o_real_filename = o_filename;
767 playlist_Export( p_playlist,
768 [o_real_filename fileSystemRepresentation],
769 p_playlist->p_local_category, "export-xspf" );
773 NSString * o_real_filename;
775 range.location = [o_filename length] - [@".html" length];
776 range.length = [@".html" length];
778 if( [o_filename compare:@".html" options: NSCaseInsensitiveSearch
779 range: range] != NSOrderedSame )
781 o_real_filename = [NSString stringWithFormat: @"%@.html", o_filename];
785 o_real_filename = o_filename;
787 playlist_Export( p_playlist,
788 [o_real_filename fileSystemRepresentation],
789 p_playlist->p_local_category, "export-html" );
794 /* When called retrieves the selected outlineview row and plays that node or item */
795 - (IBAction)playItem:(id)sender
797 intf_thread_t * p_intf = VLCIntf;
798 playlist_t * p_playlist = pl_Get( p_intf );
800 playlist_item_t *p_item;
801 playlist_item_t *p_node = NULL;
803 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
808 if( p_item->i_children == -1 )
810 p_node = p_item->p_parent;
815 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
817 p_item = p_node->pp_children[0];
824 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item );
829 - (IBAction)revealItemInFinder:(id)sender
831 playlist_item_t * p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
832 NSMutableString * o_mrl = nil;
834 if(! p_item || !p_item->p_input )
837 char *psz_uri = decode_URI( input_item_GetURI( p_item->p_input ) );
839 o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
841 /* perform some checks whether it is a file and if it is local at all... */
842 NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
843 if( prefix_range.location != NSNotFound )
844 [o_mrl deleteCharactersInRange: prefix_range];
846 if( [o_mrl characterAtIndex:0] == '/' )
847 [[NSWorkspace sharedWorkspace] selectFile: o_mrl inFileViewerRootedAtPath: o_mrl];
850 /* When called retrieves the selected outlineview row and plays that node or item */
851 - (IBAction)preparseItem:(id)sender
854 NSIndexSet *o_selected_indexes;
855 intf_thread_t * p_intf = VLCIntf;
856 playlist_t * p_playlist = pl_Get( p_intf );
857 playlist_item_t *p_item = NULL;
859 o_selected_indexes = [o_outline_view selectedRowIndexes];
860 i_count = [o_selected_indexes count];
862 NSUInteger indexes[i_count];
863 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
864 for (int i = 0; i < i_count; i++)
866 p_item = [[o_outline_view itemAtRow:indexes[i]] pointerValue];
867 [o_outline_view deselectRow: indexes[i]];
871 if( p_item->i_children == -1 )
872 playlist_PreparseEnqueue( p_playlist, p_item->p_input );
874 msg_Dbg( p_intf, "preparsing nodes not implemented" );
877 [self playlistUpdated];
880 - (IBAction)downloadCoverArt:(id)sender
883 NSIndexSet *o_selected_indexes;
884 intf_thread_t * p_intf = VLCIntf;
885 playlist_t * p_playlist = pl_Get( p_intf );
886 playlist_item_t *p_item = NULL;
888 o_selected_indexes = [o_outline_view selectedRowIndexes];
889 i_count = [o_selected_indexes count];
891 NSUInteger indexes[i_count];
892 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
893 for (int i = 0; i < i_count; i++)
895 p_item = [[o_outline_view itemAtRow: indexes[i]] pointerValue];
896 [o_outline_view deselectRow: indexes[i]];
898 if( p_item && p_item->i_children == -1 )
899 playlist_AskForArtEnqueue( p_playlist, p_item->p_input );
901 [self playlistUpdated];
904 - (IBAction)servicesChange:(id)sender
906 NSMenuItem *o_mi = (NSMenuItem *)sender;
907 NSString *o_string = [o_mi representedObject];
908 playlist_t * p_playlist = pl_Get( VLCIntf );
909 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string UTF8String] ) )
910 playlist_ServicesDiscoveryAdd( p_playlist, [o_string UTF8String] );
912 playlist_ServicesDiscoveryRemove( p_playlist, [o_string UTF8String] );
914 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
915 [o_string UTF8String] ) ? YES : NO];
917 [self playlistUpdated];
921 - (IBAction)selectAll:(id)sender
923 [o_outline_view selectAll: nil];
926 - (IBAction)deleteItem:(id)sender
929 NSIndexSet *o_selected_indexes;
930 playlist_t * p_playlist;
931 intf_thread_t * p_intf = VLCIntf;
933 o_selected_indexes = [o_outline_view selectedRowIndexes];
934 i_count = [o_selected_indexes count];
936 p_playlist = pl_Get( p_intf );
938 NSUInteger indexes[i_count];
939 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
940 for (int i = 0; i < i_count; i++)
942 id o_item = [o_outline_view itemAtRow: indexes[i]];
943 [o_outline_view deselectRow: indexes[i]];
946 playlist_item_t *p_item = [o_item pointerValue];
948 msg_Dbg( p_intf, "deleting item %i (of %i) with id \"%i\", pointerValue \"%p\" and %i children", i+1, i_count,
949 p_item->p_input->i_id, [o_item pointerValue], p_item->i_children +1 );
952 if( p_item->i_children != -1 )
953 //is a node and not an item
955 if( playlist_Status( p_playlist ) != PLAYLIST_STOPPED &&
956 [self isItem: playlist_CurrentPlayingItem( p_playlist ) inNode: ((playlist_item_t *)[o_item pointerValue])
957 checkItemExistence: NO locked:YES] == YES )
958 // if current item is in selected node and is playing then stop playlist
959 playlist_Control(p_playlist, PLAYLIST_STOP, pl_Locked );
961 playlist_NodeDelete( p_playlist, p_item, true, false );
964 playlist_DeleteFromInput( p_playlist, p_item->p_input, pl_Locked );
967 [o_outline_dict removeObjectForKey:[NSString stringWithFormat:@"%p", [o_item pointerValue]]];
971 [self playlistUpdated];
974 - (IBAction)sortNodeByName:(id)sender
976 [self sortNode: SORT_TITLE];
979 - (IBAction)sortNodeByAuthor:(id)sender
981 [self sortNode: SORT_ARTIST];
984 - (void)sortNode:(int)i_mode
986 playlist_t * p_playlist = pl_Get( VLCIntf );
987 playlist_item_t * p_item;
989 if( [o_outline_view selectedRow] > -1 )
991 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]] pointerValue];
994 /*If no item is selected, sort the whole playlist*/
996 p_item = [self currentPlaylistRoot];
1000 if( p_item->i_children > -1 ) // the item is a node
1002 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
1006 playlist_RecursiveNodeSort( p_playlist,
1007 p_item->p_parent, i_mode, ORDER_NORMAL );
1010 [self playlistUpdated];
1013 - (input_item_t *)createItem:(NSDictionary *)o_one_item
1015 intf_thread_t * p_intf = VLCIntf;
1016 playlist_t * p_playlist = pl_Get( p_intf );
1018 input_item_t *p_input;
1019 BOOL b_rem = FALSE, b_dir = FALSE;
1020 NSString *o_uri, *o_name;
1025 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1026 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1027 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1029 /* Find the name for a disc entry (i know, can you believe the trouble?) */
1030 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
1033 struct statfs *mounts = NULL;
1035 i_count = getmntinfo (&mounts, MNT_NOWAIT);
1036 /* getmntinfo returns a pointer to static data. Do not free. */
1037 for( int i_index = 0 ; i_index < i_count; i_index++ )
1039 NSMutableString *o_temp, *o_temp2;
1040 o_temp = [NSMutableString stringWithString: o_uri];
1041 o_temp2 = [NSMutableString stringWithUTF8String: mounts[i_index].f_mntfromname];
1042 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
1043 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp2 length]) ];
1044 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp2 length]) ];
1046 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
1048 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithUTF8String:mounts[i_index].f_mntonname]];
1053 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1054 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1055 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1057 /* All of this is to make sure CD's play when you D&D them on VLC */
1058 /* Converts mountpoint to a /dev file */
1061 NSMutableString *o_temp;
1063 buf = (struct statfs *) malloc (sizeof(struct statfs));
1064 statfs( [o_uri fileSystemRepresentation], buf );
1065 psz_dev = strdup(buf->f_mntfromname);
1066 o_temp = [NSMutableString stringWithUTF8String: psz_dev ];
1067 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
1068 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
1069 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NSLiteralSearch range:NSMakeRange(0, [o_temp length]) ];
1073 p_input = input_item_New( [o_uri fileSystemRepresentation], o_name ? [o_name UTF8String] : NULL );
1079 NSUInteger count = [o_options count];
1080 for( NSUInteger i = 0; i < count; i++ )
1082 input_item_AddOption( p_input, [[o_options objectAtIndex:i] UTF8String],
1083 VLC_INPUT_OPTION_TRUSTED );
1087 /* Recent documents menu */
1088 o_true_file = [NSURL URLWithString: o_uri];
1089 if( o_true_file != nil && (BOOL)config_GetInt( p_playlist, "macosx-recentitems" ) == YES )
1091 [[NSDocumentController sharedDocumentController]
1092 noteNewRecentDocumentURL: o_true_file];
1097 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1099 playlist_t * p_playlist = pl_Get( VLCIntf );
1100 NSUInteger count = [o_array count];
1101 BOOL b_usingPlaylist;
1102 if ([self currentPlaylistRoot] == p_playlist->p_ml_category)
1103 b_usingPlaylist = NO;
1105 b_usingPlaylist = YES;
1108 for( NSUInteger i_item = 0; i_item < count; i_item++ )
1110 input_item_t *p_input;
1111 NSDictionary *o_one_item;
1114 o_one_item = [o_array objectAtIndex: i_item];
1115 p_input = [self createItem: o_one_item];
1122 /* FIXME: playlist_AddInput() can fail */
1124 playlist_AddInput( p_playlist, p_input, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item, b_usingPlaylist,
1127 if( i_item == 0 && !b_enqueue )
1129 playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input );
1130 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_item->p_parent, p_item );
1133 vlc_gc_decref( p_input );
1136 [self playlistUpdated];
1139 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position enqueue:(BOOL)b_enqueue
1141 playlist_t * p_playlist = pl_Get( VLCIntf );
1142 NSUInteger count = [o_array count];
1144 for( NSUInteger i_item = 0; i_item < count; i_item++ )
1146 input_item_t *p_input;
1147 NSDictionary *o_one_item;
1150 o_one_item = [o_array objectAtIndex: i_item];
1151 p_input = [self createItem: o_one_item];
1153 if( !p_input ) continue;
1157 playlist_NodeAddInput( p_playlist, p_input, p_node,
1160 PLAYLIST_END : i_position + i_item,
1164 if( i_item == 0 && !b_enqueue )
1166 playlist_item_t *p_item;
1167 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1168 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item );
1171 vlc_gc_decref( p_input );
1173 [self playlistUpdated];
1176 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1178 playlist_t *p_playlist = pl_Get( VLCIntf );
1179 playlist_item_t *p_selected_item;
1182 i_selected_row = [o_outline_view selectedRow];
1183 if (i_selected_row < 0)
1186 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow: i_selected_row] pointerValue];
1188 for( NSUInteger i_current = 0; i_current < p_item->i_children ; i_current++ )
1191 NSString *o_current_name, *o_current_author;
1194 o_current_name = [NSString stringWithUTF8String:
1195 p_item->pp_children[i_current]->p_input->psz_name];
1196 psz_temp = input_item_GetInfo( p_item->p_input ,
1197 _("Meta-information"),_("Artist") );
1198 o_current_author = [NSString stringWithUTF8String: psz_temp];
1202 if( p_selected_item == p_item->pp_children[i_current] &&
1203 b_selected_item_met == NO )
1205 b_selected_item_met = YES;
1207 else if( p_selected_item == p_item->pp_children[i_current] &&
1208 b_selected_item_met == YES )
1212 else if( b_selected_item_met == YES &&
1213 ( [o_current_name rangeOfString:[o_search_field
1214 stringValue] options:NSCaseInsensitiveSearch].length ||
1215 [o_current_author rangeOfString:[o_search_field
1216 stringValue] options:NSCaseInsensitiveSearch].length ) )
1218 /*Adds the parent items in the result array as well, so that we can
1220 return [NSMutableArray arrayWithObject: [NSValue
1221 valueWithPointer: p_item->pp_children[i_current]]];
1223 if( p_item->pp_children[i_current]->i_children > 0 )
1225 id o_result = [self subSearchItem:
1226 p_item->pp_children[i_current]];
1227 if( o_result != NULL )
1229 [o_result insertObject: [NSValue valueWithPointer:
1230 p_item->pp_children[i_current]] atIndex:0];
1238 - (IBAction)searchItem:(id)sender
1240 playlist_t * p_playlist = pl_Get( VLCIntf );
1245 b_selected_item_met = NO;
1247 /*First, only search after the selected item:*
1248 *(b_selected_item_met = NO) */
1249 o_result = [self subSearchItem:[self currentPlaylistRoot]];
1250 if( o_result == NULL )
1252 /* If the first search failed, search again from the beginning */
1253 o_result = [self subSearchItem:[self currentPlaylistRoot]];
1255 if( o_result != NULL )
1258 if( [[o_result objectAtIndex: 0] pointerValue] == p_playlist->p_local_category )
1262 NSUInteger count = [o_result count];
1264 for( NSUInteger i = i_start ; i < count - 1 ; i++ )
1266 [o_outline_view expandItem: [o_outline_dict objectForKey:
1267 [NSString stringWithFormat: @"%p",
1268 [[o_result objectAtIndex: i] pointerValue]]]];
1270 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1271 [NSString stringWithFormat: @"%p",
1272 [[o_result objectAtIndex: count - 1 ]
1277 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_row] byExtendingSelection:NO];
1278 [o_outline_view scrollRowToVisible: i_row];
1282 - (IBAction)recursiveExpandNode:(id)sender
1284 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1285 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1287 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1288 isItemExpandable: o_item] )
1290 o_item = [o_outline_dict objectForKey: [NSString stringWithFormat: @"%p", p_item->p_parent]];
1293 /* We need to collapse the node first, since OSX refuses to recursively
1294 expand an already expanded node, even if children nodes are collapsed. */
1295 [o_outline_view collapseItem: o_item collapseChildren: YES];
1296 [o_outline_view expandItem: o_item expandChildren: YES];
1299 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1305 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1307 int row = [o_outline_view rowAtPoint:pt];
1309 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
1311 b_item_sel = ( row != -1 && [o_outline_view selectedRow] != -1 );
1312 b_rows = [o_outline_view numberOfRows] != 0;
1314 [o_mi_play setEnabled: b_item_sel];
1315 [o_mi_delete setEnabled: b_item_sel];
1316 [o_mi_selectall setEnabled: b_rows];
1317 [o_mi_info setEnabled: b_item_sel];
1318 [o_mi_preparse setEnabled: b_item_sel];
1319 [o_mi_recursive_expand setEnabled: b_item_sel];
1320 [o_mi_sort_name setEnabled: b_item_sel];
1321 [o_mi_sort_author setEnabled: b_item_sel];
1323 return( o_ctx_menu );
1326 - (void)outlineView: (NSOutlineView *)o_tv
1327 didClickTableColumn:(NSTableColumn *)o_tc
1329 int i_mode, i_type = 0;
1330 intf_thread_t *p_intf = VLCIntf;
1332 playlist_t *p_playlist = pl_Get( p_intf );
1334 /* Check whether the selected table column header corresponds to a
1335 sortable table column*/
1336 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1341 if( o_tc_sortColumn == o_tc )
1343 b_isSortDescending = !b_isSortDescending;
1347 b_isSortDescending = false;
1350 if( o_tc == o_tc_name )
1352 i_mode = SORT_TITLE;
1354 else if( o_tc == o_tc_author )
1356 i_mode = SORT_ARTIST;
1359 if( b_isSortDescending )
1361 i_type = ORDER_REVERSE;
1365 i_type = ORDER_NORMAL;
1369 playlist_RecursiveNodeSort( p_playlist, [self currentPlaylistRoot], i_mode, i_type );
1372 [self playlistUpdated];
1374 o_tc_sortColumn = o_tc;
1375 [o_outline_view setHighlightedTableColumn:o_tc];
1377 if( b_isSortDescending )
1379 [o_outline_view setIndicatorImage:o_descendingSortingImage
1380 inTableColumn:o_tc];
1384 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1385 inTableColumn:o_tc];
1390 - (void)outlineView:(NSOutlineView *)outlineView
1391 willDisplayCell:(id)cell
1392 forTableColumn:(NSTableColumn *)tableColumn
1395 playlist_t *p_playlist = pl_Get( VLCIntf );
1400 o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem( p_playlist )]];
1403 if( [self isItem: [o_playing_item pointerValue] inNode:
1404 [item pointerValue] checkItemExistence: YES]
1405 || [o_playing_item isEqual: item] )
1407 [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toHaveTrait:NSBoldFontMask]];
1411 [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toNotHaveTrait:NSBoldFontMask]];
1417 playlist_t *p_playlist = pl_Get( VLCIntf );
1422 o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem( p_playlist )]];
1425 return o_playing_item;
1429 @implementation VLCPlaylist (NSOutlineViewDataSource)
1431 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
1433 id o_value = [super outlineView: outlineView child: index ofItem: item];
1435 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p", [o_value pointerValue]]];
1439 /* Required for drag & drop and reordering */
1440 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1442 playlist_t *p_playlist = pl_Get( VLCIntf );
1444 /* First remove the items that were moved during the last drag & drop
1446 [o_items_array removeAllObjects];
1447 [o_nodes_array removeAllObjects];
1449 NSUInteger itemCount = [items count];
1451 for( NSUInteger i = 0 ; i < itemCount ; i++ )
1453 id o_item = [items objectAtIndex: i];
1455 /* Refuse to move items that are not in the General Node
1456 (Service Discovery) */
1457 if( (![self isItem: [o_item pointerValue] inNode: p_playlist->p_local_category checkItemExistence: NO] &&
1458 var_CreateGetBool( p_playlist, "media-library" ) && ![self isItem: [o_item pointerValue] inNode: p_playlist->p_ml_category checkItemExistence: NO]) ||
1459 [o_item pointerValue] == p_playlist->p_local_category ||
1460 [o_item pointerValue] == p_playlist->p_ml_category )
1464 /* Fill the items and nodes to move in 2 different arrays */
1465 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1466 [o_nodes_array addObject: o_item];
1468 [o_items_array addObject: o_item];
1471 /* Now we need to check if there are selected items that are in already
1472 selected nodes. In that case, we only want to move the nodes */
1473 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1474 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1476 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1477 a Drop operation coming from the playlist. */
1479 [pboard declareTypes: [NSArray arrayWithObjects:
1480 @"VLCPlaylistItemPboardType", nil] owner: self];
1481 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1486 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1488 playlist_t *p_playlist = pl_Get( VLCIntf );
1489 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1491 if( !p_playlist ) return NSDragOperationNone;
1493 /* Dropping ON items is not allowed if item is not a node */
1496 if( index == NSOutlineViewDropOnItemIndex &&
1497 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1499 return NSDragOperationNone;
1503 /* We refuse to drop an item in anything else than a child of the General
1504 Node. We still accept items that would be root nodes of the outlineview
1505 however, to allow drop in an empty playlist. */
1506 if( !( ([self isItem: [item pointerValue] inNode: p_playlist->p_local_category checkItemExistence: NO] ||
1507 ( var_CreateGetBool( p_playlist, "media-library" ) && [self isItem: [item pointerValue] inNode: p_playlist->p_ml_category checkItemExistence: NO] ) ) || item == nil ) )
1509 return NSDragOperationNone;
1512 /* Drop from the Playlist */
1513 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1515 NSUInteger count = [o_nodes_array count];
1516 for( NSUInteger i = 0 ; i < count ; i++ )
1518 /* We refuse to Drop in a child of an item we are moving */
1519 if( [self isItem: [item pointerValue] inNode:
1520 [[o_nodes_array objectAtIndex: i] pointerValue]
1521 checkItemExistence: NO] )
1523 return NSDragOperationNone;
1526 return NSDragOperationMove;
1529 /* Drop from the Finder */
1530 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1532 return NSDragOperationGeneric;
1534 return NSDragOperationNone;
1537 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1539 playlist_t * p_playlist = pl_Get( VLCIntf );
1540 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1542 /* Drag & Drop inside the playlist */
1543 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1545 int i_row, i_removed_from_node = 0;
1546 playlist_item_t *p_new_parent, *p_item = NULL;
1547 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1549 /* If the item is to be dropped as root item of the outline, make it a
1550 child of the respective general node, if is either the pl or the ml
1551 Else, choose the proposed parent as parent. */
1554 if ([self currentPlaylistRoot] == p_playlist->p_local_category || [self currentPlaylistRoot] == p_playlist->p_ml_category)
1555 p_new_parent = [self currentPlaylistRoot];
1557 p_new_parent = p_playlist->p_local_category;
1560 p_new_parent = [item pointerValue];
1562 /* Make sure the proposed parent is a node.
1563 (This should never be true) */
1564 if( p_new_parent->i_children < 0 )
1569 NSUInteger count = [o_all_items count];
1570 for( NSUInteger i = 0; i < count; i++ )
1572 playlist_item_t *p_old_parent = NULL;
1573 int i_old_index = 0;
1575 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1576 p_old_parent = p_item->p_parent;
1579 /* We may need the old index later */
1580 if( p_new_parent == p_old_parent )
1582 for( NSInteger j = 0; j < p_old_parent->i_children; j++ )
1584 if( p_old_parent->pp_children[j] == p_item )
1593 // Actually detach the item from the old position
1594 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1598 /* Calculate the new index */
1601 /* If we move the item in the same node, we need to take into
1602 account that one item will be deleted */
1605 if ((p_new_parent == p_old_parent && i_old_index < index + (int)i) )
1607 i_removed_from_node++;
1609 i_new_index = index + i - i_removed_from_node;
1611 // Reattach the item to the new position
1612 playlist_NodeInsert( p_playlist, p_item, p_new_parent, i_new_index );
1616 [self playlistUpdated];
1617 i_row = [o_outline_view rowForItem:[o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", [[o_all_items objectAtIndex: 0] pointerValue]]]];
1621 i_row = [o_outline_view rowForItem:[o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1624 [o_outline_view deselectAll: self];
1625 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_row] byExtendingSelection:NO];
1626 [o_outline_view scrollRowToVisible: i_row];
1631 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1633 playlist_item_t *p_node = [item pointerValue];
1635 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
1636 sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1637 NSUInteger count = [o_values count];
1638 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1639 input_thread_t * p_input = pl_CurrentInput( VLCIntf );
1640 BOOL b_returned = NO;
1642 if (count == 1 && p_input)
1644 b_returned = input_AddSubtitle( p_input, make_URI([[o_values objectAtIndex:0] UTF8String], NULL), true );
1645 vlc_object_release( p_input );
1650 vlc_object_release( p_input );
1652 for( NSUInteger i = 0; i < count; i++)
1654 NSDictionary *o_dic;
1655 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
1659 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1663 [o_array addObject: o_dic];
1668 [self appendArray:o_array atPos:index enqueue: YES];
1672 assert( p_node->i_children != -1 );
1673 [self appendNodeArray:o_array inNode: p_node atPos:index enqueue:YES];