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
33 /*****************************************************************************
35 *****************************************************************************/
36 #include <stdlib.h> /* malloc(), free() */
37 #include <sys/param.h> /* for MAXPATHLEN */
40 #include <sys/mount.h>
45 #import "playlistinfo.h"
53 #import <vlc_interface.h>
58 /*****************************************************************************
59 * VLCPlaylistView implementation
60 *****************************************************************************/
61 @implementation VLCPlaylistView
63 - (NSMenu *)menuForEvent:(NSEvent *)o_event
65 return( [(VLCPlaylist *)[self delegate] menuForEvent: o_event] );
68 - (void)keyDown:(NSEvent *)o_event
72 if( [[o_event characters] length] )
74 key = [[o_event characters] characterAtIndex: 0];
79 case NSDeleteCharacter:
80 case NSDeleteFunctionKey:
81 case NSDeleteCharFunctionKey:
82 case NSBackspaceCharacter:
83 [(VLCPlaylist *)[self delegate] deleteItem:self];
86 case NSEnterCharacter:
87 case NSCarriageReturnCharacter:
88 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] playItem:self];
92 [super keyDown: o_event];
97 - (BOOL)validateMenuItem:(NSMenuItem *)item
99 if (([self numberOfSelectedRows] >= 1 && [item action] == @selector(delete:)) || [item action] == @selector(selectAll:))
105 - (BOOL) acceptsFirstResponder
110 - (BOOL) becomeFirstResponder
112 [self setNeedsDisplay:YES];
116 - (BOOL) resignFirstResponder
118 [self setNeedsDisplay:YES];
122 - (IBAction)delete:(id)sender
124 [[[VLCMain sharedInstance] playlist] deleteItem: sender];
129 /*****************************************************************************
130 * VLCPlaylistCommon implementation
132 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
133 * It contains the common methods and elements of these 2 entities.
134 *****************************************************************************/
135 @implementation VLCPlaylistCommon
139 playlist_t * p_playlist = pl_Get( VLCIntf );
140 p_current_root_item = p_playlist->p_local_category;
145 o_outline_dict = [[NSMutableDictionary alloc] init];
152 playlist_t * p_playlist = pl_Get( VLCIntf );
153 [o_outline_view setTarget: self];
154 [o_outline_view setDelegate: self];
155 [o_outline_view setDataSource: self];
156 [o_outline_view setAllowsEmptySelection: NO];
157 [o_outline_view expandItem: [o_outline_view itemAtRow:0]];
159 [o_outline_view_other setTarget: self];
160 [o_outline_view_other setDelegate: self];
161 [o_outline_view_other setDataSource: self];
162 [o_outline_view_other setAllowsEmptySelection: NO];
169 [[o_tc_name headerCell] setStringValue:_NS("Name")];
170 [[o_tc_author headerCell] setStringValue:_NS("Author")];
171 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
173 [[o_tc_name_other headerCell] setStringValue:_NS("Name")];
174 [[o_tc_author_other headerCell] setStringValue:_NS("Author")];
175 [[o_tc_duration_other headerCell] setStringValue:_NS("Duration")];
178 - (void)setPlaylistRoot: (playlist_item_t *)root_item
180 p_current_root_item = root_item;
181 [o_outline_view reloadData];
182 [o_outline_view_other reloadData];
185 - (playlist_item_t *)currentPlaylistRoot
187 return p_current_root_item;
190 - (void)swapPlaylists:(id)newList
192 if(newList != o_outline_view)
194 id o_outline_view_temp = o_outline_view;
195 id o_tc_author_temp = o_tc_author;
196 id o_tc_duration_temp = o_tc_duration;
197 id o_tc_name_temp = o_tc_name;
198 o_outline_view = o_outline_view_other;
199 o_tc_author = o_tc_author_other;
200 o_tc_duration = o_tc_duration_other;
201 o_tc_name = o_tc_name_other;
202 o_outline_view_other = o_outline_view_temp;
203 o_tc_author_other = o_tc_author_temp;
204 o_tc_duration_other = o_tc_duration_temp;
205 o_tc_name_other = o_tc_name_temp;
209 - (NSOutlineView *)outlineView
211 return o_outline_view;
214 - (playlist_item_t *)selectedPlaylistItem
216 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
222 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
223 /* return the number of children for Obj-C pointer item */ /* DONE */
224 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
227 playlist_item_t *p_item = NULL;
228 playlist_t * p_playlist = pl_Get( VLCIntf );
229 //assert( outlineView == o_outline_view );
234 p_item = p_current_root_item;
237 p_item = (playlist_item_t *)[item pointerValue];
240 i_return = p_item->i_children;
243 return i_return > 0 ? i_return : 0;
246 /* return the child at index for the Obj-C pointer item */ /* DONE */
247 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
249 playlist_item_t *p_return = NULL, *p_item = NULL;
251 playlist_t * p_playlist = pl_Get( VLCIntf );
257 p_item = p_current_root_item;
261 p_item = (playlist_item_t *)[item pointerValue];
263 if( p_item && index < p_item->i_children && index >= 0 )
264 p_return = p_item->pp_children[index];
267 o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
271 /* FIXME: Why is there a warning if that happens all the time and seems
272 * to be normal? Add an assert and fix it.
273 * msg_Warn( VLCIntf, "playlist item misses pointer value, adding one" ); */
274 o_value = [[NSValue valueWithPointer: p_return] retain];
279 /* is the item expandable */
280 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
283 playlist_t *p_playlist = pl_Get( VLCIntf );
289 if( p_current_root_item )
291 i_return = p_current_root_item->i_children;
296 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
298 i_return = p_item->i_children;
302 return (i_return >= 0);
305 /* retrieve the string values for the cells */
306 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
309 playlist_item_t *p_item;
311 /* For error handling */
312 static BOOL attempted_reload = NO;
314 if( item == nil || ![item isKindOfClass: [NSValue class]] )
316 /* Attempt to fix the error by asking for a data redisplay
317 * This might cause infinite loop, so add a small check */
318 if( !attempted_reload )
320 attempted_reload = YES;
321 [outlineView reloadData];
326 p_item = (playlist_item_t *)[item pointerValue];
327 if( !p_item || !p_item->p_input )
329 /* Attempt to fix the error by asking for a data redisplay
330 * This might cause infinite loop, so add a small check */
331 if( !attempted_reload )
333 attempted_reload = YES;
334 [outlineView reloadData];
339 attempted_reload = NO;
341 if( [[o_tc identifier] isEqualToString:@"name"] )
343 /* sanity check to prevent the NSString class from crashing */
344 char *psz_title = input_item_GetTitleFbName( p_item->p_input );
347 o_value = [NSString stringWithUTF8String: psz_title];
351 else if( [[o_tc identifier] isEqualToString:@"artist"] )
353 char *psz_artist = input_item_GetArtist( p_item->p_input );
355 o_value = [NSString stringWithUTF8String: psz_artist];
358 else if( [[o_tc identifier] isEqualToString:@"duration"] )
360 char psz_duration[MSTRTIME_MAX_SIZE];
361 mtime_t dur = input_item_GetDuration( p_item->p_input );
364 secstotimestr( psz_duration, dur/1000000 );
365 o_value = [NSString stringWithUTF8String: psz_duration];
370 else if( [[o_tc identifier] isEqualToString:@"status"] )
372 if( input_item_HasErrorWhenReading( p_item->p_input ) )
374 o_value = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kAlertCautionIcon)];
375 [o_value setSize: NSMakeSize(16,16)];
383 /*****************************************************************************
384 * VLCPlaylistWizard implementation
385 *****************************************************************************/
386 @implementation VLCPlaylistWizard
388 - (IBAction)reloadOutlineView
390 /* Only reload the outlineview if the wizard window is open since this can
391 be quite long on big playlists */
392 if( [[o_outline_view window] isVisible] )
394 [o_outline_view reloadData];
400 /*****************************************************************************
401 * extension to NSOutlineView's interface to fix compilation warnings
402 * and let us access these 2 functions properly
403 * this uses a private Apple-API, but works fine on all current OSX releases
404 * keep checking for compatiblity with future releases though
405 *****************************************************************************/
407 @interface NSOutlineView (UndocumentedSortImages)
408 + (NSImage *)_defaultTableHeaderSortImage;
409 + (NSImage *)_defaultTableHeaderReverseSortImage;
413 /*****************************************************************************
414 * VLCPlaylist implementation
415 *****************************************************************************/
416 @implementation VLCPlaylist
423 o_nodes_array = [[NSMutableArray alloc] init];
424 o_items_array = [[NSMutableArray alloc] init];
431 [o_nodes_array release];
432 [o_items_array release];
438 playlist_t * p_playlist = pl_Get( VLCIntf );
442 [super awakeFromNib];
444 [o_outline_view setDoubleAction: @selector(playItem:)];
445 [o_outline_view_other setDoubleAction: @selector(playItem:)];
447 [o_outline_view registerForDraggedTypes:
448 [NSArray arrayWithObjects: NSFilenamesPboardType,
449 @"VLCPlaylistItemPboardType", nil]];
450 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
452 [o_outline_view_other registerForDraggedTypes:
453 [NSArray arrayWithObjects: NSFilenamesPboardType,
454 @"VLCPlaylistItemPboardType", nil]];
455 [o_outline_view_other setIntercellSpacing: NSMakeSize (0.0, 1.0)];
457 /* This uses private Apple API which works fine until 10.5.
458 * We need to keep checking in the future!
459 * These methods are being added artificially to NSOutlineView's interface above */
460 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
461 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
463 o_tc_sortColumn = nil;
466 - (void)searchfieldChanged:(NSNotification *)o_notification
468 [o_search_field setStringValue:[[o_notification object] stringValue]];
475 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
476 [o_mi_play setTitle: _NS("Play")];
477 [o_mi_delete setTitle: _NS("Delete")];
478 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
479 [o_mi_selectall setTitle: _NS("Select All")];
480 [o_mi_info setTitle: _NS("Media Information...")];
481 [o_mi_dl_cover_art setTitle: _NS("Download Cover Art")];
482 [o_mi_preparse setTitle: _NS("Fetch Meta Data")];
483 [o_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
484 [o_mm_mi_revealInFinder setTitle: _NS("Reveal in Finder")];
485 [[o_mm_mi_revealInFinder menu] setAutoenablesItems: NO];
486 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
487 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
489 [o_search_field setToolTip: _NS("Search in Playlist")];
490 [o_search_field_other setToolTip: _NS("Search in Playlist")];
492 [o_save_accessory_text setStringValue: _NS("File Format:")];
493 [[o_save_accessory_popup itemAtIndex:0] setTitle: _NS("Extended M3U")];
494 [[o_save_accessory_popup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
495 [[o_save_accessory_popup itemAtIndex:2] setTitle: _NS("HTML Playlist")];
498 - (void)swapPlaylists:(id)newList
500 if(newList != o_outline_view)
502 id o_search_field_temp = o_search_field;
503 o_search_field = o_search_field_other;
504 o_search_field_other = o_search_field_temp;
505 [super swapPlaylists:newList];
506 [self playlistUpdated];
510 - (void)playlistUpdated
512 /* Clear indications of any existing column sorting */
513 NSUInteger count = [[o_outline_view tableColumns] count];
514 for( NSUInteger i = 0 ; i < count ; i++ )
516 [o_outline_view setIndicatorImage:nil inTableColumn:
517 [[o_outline_view tableColumns] objectAtIndex:i]];
520 [o_outline_view setHighlightedTableColumn:nil];
521 o_tc_sortColumn = nil;
522 // TODO Find a way to keep the dict size to a minimum
523 //[o_outline_dict removeAllObjects];
524 [o_outline_view reloadData];
525 [[[[VLCMain sharedInstance] wizard] playlistWizard] reloadOutlineView];
526 [[[[VLCMain sharedInstance] bookmarks] dataTable] reloadData];
528 [self outlineViewSelectionDidChange: nil];
529 [[VLCMain sharedInstance] updateMainWindow];
532 - (void)outlineViewSelectionDidChange:(NSNotification *)notification
535 playlist_item_t * p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
539 /* update the state of our Reveal-in-Finder menu items */
540 NSMutableString *o_mrl;
541 char *psz_uri = input_item_GetURI( p_item->p_input );
543 [o_mi_revealInFinder setEnabled: NO];
544 [o_mm_mi_revealInFinder setEnabled: NO];
547 o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
549 /* perform some checks whether it is a file and if it is local at all... */
550 NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
551 if( prefix_range.location != NSNotFound )
552 [o_mrl deleteCharactersInRange: prefix_range];
554 if( [o_mrl characterAtIndex:0] == '/' )
556 [o_mi_revealInFinder setEnabled: YES];
557 [o_mm_mi_revealInFinder setEnabled: YES];
562 /* update our info-panel to reflect the new item */
563 [[[VLCMain sharedInstance] info] updatePanelWithItem:p_item->p_input];
567 - (BOOL)isSelectionEmpty
569 return [o_outline_view selectedRow] == -1;
572 - (void)updateRowSelection
575 playlist_t *p_playlist = pl_Get( VLCIntf );
576 playlist_item_t *p_item, *p_temp_item;
577 NSMutableArray *o_array = [NSMutableArray array];
580 p_item = playlist_CurrentPlayingItem( p_playlist );
587 p_temp_item = p_item;
588 while( p_temp_item->p_parent )
590 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
591 p_temp_item = p_temp_item->p_parent;
595 NSUInteger count = [o_array count];
596 for( NSUInteger j = 0; j < count - 1; j++ )
599 if( ( o_item = [o_outline_dict objectForKey:
600 [NSString stringWithFormat: @"%p",
601 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
603 [o_outline_view expandItem: o_item];
607 id o_item = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_item]];
608 NSInteger i_index = [o_outline_view rowForItem:o_item];
609 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_index] byExtendingSelection:NO];
610 [o_outline_view setNeedsDisplay:YES];
613 /* Check if p_item is a child of p_node recursively. We need to check the item
614 existence first since OSX sometimes tries to redraw items that have been
615 deleted. We don't do it when not required since this verification takes
616 quite a long time on big playlists (yes, pretty hacky). */
618 - (BOOL)isItem: (playlist_item_t *)p_item
619 inNode: (playlist_item_t *)p_node
620 checkItemExistence:(BOOL)b_check
621 locked:(BOOL)b_locked
624 playlist_t * p_playlist = pl_Get( VLCIntf );
625 playlist_item_t *p_temp_item = p_item;
627 if( p_node == p_item )
630 if( p_node->i_children < 1)
636 if(!b_locked) PL_LOCK;
640 /* Since outlineView: willDisplayCell:... may call this function with
641 p_items that don't exist anymore, first check if the item is still
642 in the playlist. Any cleaner solution welcomed. */
643 for( i = 0; i < p_playlist->all_items.i_size; i++ )
645 if( ARRAY_VAL( p_playlist->all_items, i) == p_item ) break;
646 else if ( i == p_playlist->all_items.i_size - 1 )
648 if(!b_locked) PL_UNLOCK;
656 p_temp_item = p_temp_item->p_parent;
657 if( p_temp_item == p_node )
659 if(!b_locked) PL_UNLOCK;
663 if(!b_locked) PL_UNLOCK;
668 - (BOOL)isItem: (playlist_item_t *)p_item
669 inNode: (playlist_item_t *)p_node
670 checkItemExistence:(BOOL)b_check
672 return [self isItem:p_item inNode:p_node checkItemExistence:b_check locked:NO];
675 /* This method is useful for instance to remove the selected children of an
676 already selected node */
677 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
679 NSUInteger itemCount = [o_items count];
680 NSUInteger nodeCount = [o_nodes count];
681 for( NSUInteger i = 0 ; i < itemCount ; i++ )
683 for ( NSUInteger j = 0 ; j < nodeCount ; j++ )
685 if( o_items == o_nodes)
687 if( j == i ) continue;
689 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
690 inNode: [[o_nodes objectAtIndex:j] pointerValue]
691 checkItemExistence: NO locked:NO] )
693 [o_items removeObjectAtIndex:i];
694 /* We need to execute the next iteration with the same index
695 since the current item has been deleted */
703 - (IBAction)savePlaylist:(id)sender
705 playlist_t * p_playlist = pl_Get( VLCIntf );
707 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
708 NSString * o_name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
710 [o_save_panel setTitle: _NS("Save Playlist")];
711 [o_save_panel setPrompt: _NS("Save")];
712 [o_save_panel setAccessoryView: o_save_accessory_view];
714 if( [o_save_panel runModalForDirectory: nil
715 file: o_name] == NSOKButton )
717 NSString *o_filename = [[o_save_panel URL] path];
719 if( [o_save_accessory_popup indexOfSelectedItem] == 0 )
721 NSString * o_real_filename;
723 range.location = [o_filename length] - [@".m3u" length];
724 range.length = [@".m3u" length];
726 if( [o_filename compare:@".m3u" options: NSCaseInsensitiveSearch
727 range: range] != NSOrderedSame )
729 o_real_filename = [NSString stringWithFormat: @"%@.m3u", o_filename];
733 o_real_filename = o_filename;
735 playlist_Export( p_playlist,
736 [o_real_filename fileSystemRepresentation],
737 p_playlist->p_local_category, "export-m3u" );
739 else if( [o_save_accessory_popup indexOfSelectedItem] == 1 )
741 NSString * o_real_filename;
743 range.location = [o_filename length] - [@".xspf" length];
744 range.length = [@".xspf" length];
746 if( [o_filename compare:@".xspf" options: NSCaseInsensitiveSearch
747 range: range] != NSOrderedSame )
749 o_real_filename = [NSString stringWithFormat: @"%@.xspf", o_filename];
753 o_real_filename = o_filename;
755 playlist_Export( p_playlist,
756 [o_real_filename fileSystemRepresentation],
757 p_playlist->p_local_category, "export-xspf" );
761 NSString * o_real_filename;
763 range.location = [o_filename length] - [@".html" length];
764 range.length = [@".html" length];
766 if( [o_filename compare:@".html" options: NSCaseInsensitiveSearch
767 range: range] != NSOrderedSame )
769 o_real_filename = [NSString stringWithFormat: @"%@.html", o_filename];
773 o_real_filename = o_filename;
775 playlist_Export( p_playlist,
776 [o_real_filename fileSystemRepresentation],
777 p_playlist->p_local_category, "export-html" );
782 /* When called retrieves the selected outlineview row and plays that node or item */
783 - (IBAction)playItem:(id)sender
785 intf_thread_t * p_intf = VLCIntf;
786 playlist_t * p_playlist = pl_Get( p_intf );
788 playlist_item_t *p_item;
789 playlist_item_t *p_node = NULL;
791 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
796 if( p_item->i_children == -1 )
798 p_node = p_item->p_parent;
803 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
805 p_item = p_node->pp_children[0];
812 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item );
817 - (IBAction)revealItemInFinder:(id)sender
819 playlist_item_t * p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
820 NSMutableString * o_mrl = nil;
822 if(! p_item || !p_item->p_input )
825 char *psz_uri = decode_URI( input_item_GetURI( p_item->p_input ) );
827 o_mrl = [NSMutableString stringWithUTF8String: psz_uri];
829 /* perform some checks whether it is a file and if it is local at all... */
830 NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
831 if( prefix_range.location != NSNotFound )
832 [o_mrl deleteCharactersInRange: prefix_range];
834 if( [o_mrl characterAtIndex:0] == '/' )
835 [[NSWorkspace sharedWorkspace] selectFile: o_mrl inFileViewerRootedAtPath: o_mrl];
838 /* When called retrieves the selected outlineview row and plays that node or item */
839 - (IBAction)preparseItem:(id)sender
842 NSIndexSet *o_selected_indexes;
843 intf_thread_t * p_intf = VLCIntf;
844 playlist_t * p_playlist = pl_Get( p_intf );
845 playlist_item_t *p_item = NULL;
847 o_selected_indexes = [o_outline_view selectedRowIndexes];
848 i_count = [o_selected_indexes count];
850 NSUInteger indexes[i_count];
851 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
852 for (int i = 0; i < i_count; i++)
854 p_item = [[o_outline_view itemAtRow:indexes[i]] pointerValue];
855 [o_outline_view deselectRow: indexes[i]];
859 if( p_item->i_children == -1 )
860 playlist_PreparseEnqueue( p_playlist, p_item->p_input );
862 msg_Dbg( p_intf, "preparsing nodes not implemented" );
865 [self playlistUpdated];
868 - (IBAction)downloadCoverArt:(id)sender
871 NSIndexSet *o_selected_indexes;
872 intf_thread_t * p_intf = VLCIntf;
873 playlist_t * p_playlist = pl_Get( p_intf );
874 playlist_item_t *p_item = NULL;
876 o_selected_indexes = [o_outline_view selectedRowIndexes];
877 i_count = [o_selected_indexes count];
879 NSUInteger indexes[i_count];
880 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
881 for (int i = 0; i < i_count; i++)
883 p_item = [[o_outline_view itemAtRow: indexes[i]] pointerValue];
884 [o_outline_view deselectRow: indexes[i]];
886 if( p_item && p_item->i_children == -1 )
887 playlist_AskForArtEnqueue( p_playlist, p_item->p_input );
889 [self playlistUpdated];
892 - (IBAction)selectAll:(id)sender
894 [o_outline_view selectAll: nil];
897 - (IBAction)deleteItem:(id)sender
900 NSIndexSet *o_selected_indexes;
901 playlist_t * p_playlist;
902 intf_thread_t * p_intf = VLCIntf;
904 o_selected_indexes = [o_outline_view selectedRowIndexes];
905 i_count = [o_selected_indexes count];
907 p_playlist = pl_Get( p_intf );
909 NSUInteger indexes[i_count];
910 if (i_count == [o_outline_view numberOfRows])
913 msg_Dbg( p_intf, "user selected entire list, deleting current playlist root instead of individual items" );
916 playlist_NodeDelete( p_playlist, [self currentPlaylistRoot], true, false );
918 [self playlistUpdated];
921 [o_selected_indexes getIndexes:indexes maxCount:i_count inIndexRange:nil];
922 for (int i = 0; i < i_count; i++)
924 id o_item = [o_outline_view itemAtRow: indexes[i]];
925 [o_outline_view deselectRow: indexes[i]];
928 playlist_item_t *p_item = [o_item pointerValue];
930 msg_Dbg( p_intf, "deleting item %i (of %i) with id \"%i\", pointerValue \"%p\" and %i children", i+1, i_count,
931 p_item->p_input->i_id, [o_item pointerValue], p_item->i_children +1 );
934 if( p_item->i_children != -1 )
935 //is a node and not an item
937 if( playlist_Status( p_playlist ) != PLAYLIST_STOPPED &&
938 [self isItem: playlist_CurrentPlayingItem( p_playlist ) inNode: ((playlist_item_t *)[o_item pointerValue])
939 checkItemExistence: NO locked:YES] == YES )
940 // if current item is in selected node and is playing then stop playlist
941 playlist_Control(p_playlist, PLAYLIST_STOP, pl_Locked );
943 playlist_NodeDelete( p_playlist, p_item, true, false );
946 playlist_DeleteFromInput( p_playlist, p_item->p_input, pl_Locked );
949 [o_outline_dict removeObjectForKey:[NSString stringWithFormat:@"%p", [o_item pointerValue]]];
953 [self playlistUpdated];
956 - (IBAction)sortNodeByName:(id)sender
958 [self sortNode: SORT_TITLE];
961 - (IBAction)sortNodeByAuthor:(id)sender
963 [self sortNode: SORT_ARTIST];
966 - (void)sortNode:(int)i_mode
968 playlist_t * p_playlist = pl_Get( VLCIntf );
969 playlist_item_t * p_item;
971 if( [o_outline_view selectedRow] > -1 )
973 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]] pointerValue];
976 /*If no item is selected, sort the whole playlist*/
978 p_item = [self currentPlaylistRoot];
982 if( p_item->i_children > -1 ) // the item is a node
984 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
988 playlist_RecursiveNodeSort( p_playlist,
989 p_item->p_parent, i_mode, ORDER_NORMAL );
992 [self playlistUpdated];
995 - (input_item_t *)createItem:(NSDictionary *)o_one_item
997 intf_thread_t * p_intf = VLCIntf;
998 playlist_t * p_playlist = pl_Get( p_intf );
1000 input_item_t *p_input;
1001 BOOL b_rem = FALSE, b_dir = FALSE, b_writable = FALSE;
1002 NSString *o_uri, *o_name, *o_path;
1008 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
1009 o_nsurl = [NSURL URLWithString: o_uri];
1010 o_path = [o_nsurl path];
1011 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
1012 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
1014 if( [[NSFileManager defaultManager] fileExistsAtPath:o_path isDirectory:&b_dir] && b_dir &&
1015 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:o_path isRemovable: &b_rem
1016 isWritable:&b_writable isUnmountable:NULL description:NULL type:NULL] && b_rem && !b_writable && [o_nsurl isFileURL] )
1019 id o_vlc_open = [[VLCMain sharedInstance] open];
1021 char *diskType = [o_vlc_open getVolumeTypeFromMountPath: o_path];
1022 msg_Dbg( p_intf, "detected optical media of type '%s' in the file input", diskType );
1024 if (diskType == kVLCMediaDVD)
1026 o_uri = [NSString stringWithFormat: @"dvdnav://%@", [o_vlc_open getBSDNodeFromMountPath: o_path]];
1028 else if (diskType == kVLCMediaVideoTSFolder)
1030 o_uri = [NSString stringWithFormat: @"dvdnav://%@", o_path];
1032 else if (diskType == kVLCMediaAudioCD)
1034 o_uri = [NSString stringWithFormat: @"cdda://%@", [o_vlc_open getBSDNodeFromMountPath: o_path]];
1036 else if (diskType == kVLCMediaVCD)
1038 o_uri = [NSString stringWithFormat: @"vcd://%@#0:0", [o_vlc_open getBSDNodeFromMountPath: o_path]];
1040 else if (diskType == kVLCMediaSVCD)
1042 o_uri = [NSString stringWithFormat: @"vcd://%@@0:0", [o_vlc_open getBSDNodeFromMountPath: o_path]];
1044 else if (diskType == kVLCMediaBD || diskType == kVLCMediaBDMVFolder)
1046 o_uri = [NSString stringWithFormat: @"bluray://%@", o_path];
1050 msg_Warn( VLCIntf, "unknown disk type, treating %s as regular input", [o_path UTF8String] );
1053 p_input = input_item_New( [o_uri UTF8String], [[[NSFileManager defaultManager] displayNameAtPath: o_path] UTF8String] );
1056 p_input = input_item_New( [o_uri fileSystemRepresentation], o_name ? [o_name UTF8String] : NULL );
1063 NSUInteger count = [o_options count];
1064 for( NSUInteger i = 0; i < count; i++ )
1066 input_item_AddOption( p_input, [[o_options objectAtIndex:i] UTF8String],
1067 VLC_INPUT_OPTION_TRUSTED );
1071 /* Recent documents menu */
1072 if( o_nsurl != nil && (BOOL)config_GetInt( p_playlist, "macosx-recentitems" ) == YES )
1074 [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: o_nsurl];
1079 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1081 playlist_t * p_playlist = pl_Get( VLCIntf );
1082 NSUInteger count = [o_array count];
1083 BOOL b_usingPlaylist;
1084 if ([self currentPlaylistRoot] == p_playlist->p_ml_category)
1085 b_usingPlaylist = NO;
1087 b_usingPlaylist = YES;
1090 for( NSUInteger i_item = 0; i_item < count; i_item++ )
1092 input_item_t *p_input;
1093 NSDictionary *o_one_item;
1096 o_one_item = [o_array objectAtIndex: i_item];
1097 p_input = [self createItem: o_one_item];
1104 /* FIXME: playlist_AddInput() can fail */
1106 playlist_AddInput( p_playlist, p_input, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item, b_usingPlaylist,
1109 if( i_item == 0 && !b_enqueue )
1111 playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input );
1112 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_item->p_parent, p_item );
1115 vlc_gc_decref( p_input );
1118 [self playlistUpdated];
1121 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position enqueue:(BOOL)b_enqueue
1123 playlist_t * p_playlist = pl_Get( VLCIntf );
1124 NSUInteger count = [o_array count];
1126 for( NSUInteger i_item = 0; i_item < count; i_item++ )
1128 input_item_t *p_input;
1129 NSDictionary *o_one_item;
1132 o_one_item = [o_array objectAtIndex: i_item];
1133 p_input = [self createItem: o_one_item];
1135 if( !p_input ) continue;
1139 playlist_NodeAddInput( p_playlist, p_input, p_node,
1142 PLAYLIST_END : i_position + i_item,
1146 if( i_item == 0 && !b_enqueue )
1148 playlist_item_t *p_item;
1149 p_item = playlist_ItemGetByInput( p_playlist, p_input );
1150 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_node, p_item );
1153 vlc_gc_decref( p_input );
1155 [self playlistUpdated];
1158 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1160 playlist_t *p_playlist = pl_Get( VLCIntf );
1161 playlist_item_t *p_selected_item;
1164 i_selected_row = [o_outline_view selectedRow];
1165 if (i_selected_row < 0)
1168 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow: i_selected_row] pointerValue];
1170 for( NSUInteger i_current = 0; i_current < p_item->i_children ; i_current++ )
1173 NSString *o_current_name, *o_current_author;
1176 o_current_name = [NSString stringWithUTF8String:
1177 p_item->pp_children[i_current]->p_input->psz_name];
1178 psz_temp = input_item_GetInfo( p_item->p_input ,
1179 _("Meta-information"),_("Artist") );
1180 o_current_author = [NSString stringWithUTF8String: psz_temp];
1184 if( p_selected_item == p_item->pp_children[i_current] &&
1185 b_selected_item_met == NO )
1187 b_selected_item_met = YES;
1189 else if( p_selected_item == p_item->pp_children[i_current] &&
1190 b_selected_item_met == YES )
1194 else if( b_selected_item_met == YES &&
1195 ( [o_current_name rangeOfString:[o_search_field
1196 stringValue] options:NSCaseInsensitiveSearch].length ||
1197 [o_current_author rangeOfString:[o_search_field
1198 stringValue] options:NSCaseInsensitiveSearch].length ) )
1200 /*Adds the parent items in the result array as well, so that we can
1202 return [NSMutableArray arrayWithObject: [NSValue
1203 valueWithPointer: p_item->pp_children[i_current]]];
1205 if( p_item->pp_children[i_current]->i_children > 0 )
1207 id o_result = [self subSearchItem:
1208 p_item->pp_children[i_current]];
1209 if( o_result != NULL )
1211 [o_result insertObject: [NSValue valueWithPointer:
1212 p_item->pp_children[i_current]] atIndex:0];
1220 - (IBAction)searchItem:(id)sender
1222 playlist_t * p_playlist = pl_Get( VLCIntf );
1227 b_selected_item_met = NO;
1229 /*First, only search after the selected item:*
1230 *(b_selected_item_met = NO) */
1231 o_result = [self subSearchItem:[self currentPlaylistRoot]];
1232 if( o_result == NULL )
1234 /* If the first search failed, search again from the beginning */
1235 o_result = [self subSearchItem:[self currentPlaylistRoot]];
1237 if( o_result != NULL )
1240 if( [[o_result objectAtIndex: 0] pointerValue] == p_playlist->p_local_category )
1244 NSUInteger count = [o_result count];
1246 for( NSUInteger i = i_start ; i < count - 1 ; i++ )
1248 [o_outline_view expandItem: [o_outline_dict objectForKey:
1249 [NSString stringWithFormat: @"%p",
1250 [[o_result objectAtIndex: i] pointerValue]]]];
1252 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1253 [NSString stringWithFormat: @"%p",
1254 [[o_result objectAtIndex: count - 1 ]
1259 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_row] byExtendingSelection:NO];
1260 [o_outline_view scrollRowToVisible: i_row];
1264 - (IBAction)recursiveExpandNode:(id)sender
1266 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1267 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1269 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1270 isItemExpandable: o_item] )
1272 o_item = [o_outline_dict objectForKey: [NSString stringWithFormat: @"%p", p_item->p_parent]];
1275 /* We need to collapse the node first, since OSX refuses to recursively
1276 expand an already expanded node, even if children nodes are collapsed. */
1277 [o_outline_view collapseItem: o_item collapseChildren: YES];
1278 [o_outline_view expandItem: o_item expandChildren: YES];
1281 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1287 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1289 int row = [o_outline_view rowAtPoint:pt];
1291 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
1293 b_item_sel = ( row != -1 && [o_outline_view selectedRow] != -1 );
1294 b_rows = [o_outline_view numberOfRows] != 0;
1296 [o_mi_play setEnabled: b_item_sel];
1297 [o_mi_delete setEnabled: b_item_sel];
1298 [o_mi_selectall setEnabled: b_rows];
1299 [o_mi_info setEnabled: b_item_sel];
1300 [o_mi_preparse setEnabled: b_item_sel];
1301 [o_mi_recursive_expand setEnabled: b_item_sel];
1302 [o_mi_sort_name setEnabled: b_item_sel];
1303 [o_mi_sort_author setEnabled: b_item_sel];
1305 return( o_ctx_menu );
1308 - (void)outlineView: (NSOutlineView *)o_tv
1309 didClickTableColumn:(NSTableColumn *)o_tc
1311 int i_mode, i_type = 0;
1312 intf_thread_t *p_intf = VLCIntf;
1314 playlist_t *p_playlist = pl_Get( p_intf );
1316 /* Check whether the selected table column header corresponds to a
1317 sortable table column*/
1318 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1323 if( o_tc_sortColumn == o_tc )
1325 b_isSortDescending = !b_isSortDescending;
1329 b_isSortDescending = false;
1332 if( o_tc == o_tc_name )
1334 i_mode = SORT_TITLE;
1336 else if( o_tc == o_tc_author )
1338 i_mode = SORT_ARTIST;
1341 if( b_isSortDescending )
1343 i_type = ORDER_REVERSE;
1347 i_type = ORDER_NORMAL;
1351 playlist_RecursiveNodeSort( p_playlist, [self currentPlaylistRoot], i_mode, i_type );
1354 [self playlistUpdated];
1356 o_tc_sortColumn = o_tc;
1357 [o_outline_view setHighlightedTableColumn:o_tc];
1359 if( b_isSortDescending )
1361 [o_outline_view setIndicatorImage:o_descendingSortingImage
1362 inTableColumn:o_tc];
1366 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1367 inTableColumn:o_tc];
1372 - (void)outlineView:(NSOutlineView *)outlineView
1373 willDisplayCell:(id)cell
1374 forTableColumn:(NSTableColumn *)tableColumn
1377 /* this method can be called when VLC is already dead, hence the extra checks */
1378 intf_thread_t * p_intf = VLCIntf;
1381 playlist_t *p_playlist = pl_Get( p_intf );
1388 o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem( p_playlist )]];
1391 if( [self isItem: [o_playing_item pointerValue] inNode:
1392 [item pointerValue] checkItemExistence: YES]
1393 || [o_playing_item isEqual: item] )
1395 [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toHaveTrait:NSBoldFontMask]];
1399 [cell setFont: [[NSFontManager sharedFontManager] convertFont:[cell font] toNotHaveTrait:NSBoldFontMask]];
1405 playlist_t *p_playlist = pl_Get( VLCIntf );
1410 o_playing_item = [o_outline_dict objectForKey: [NSString stringWithFormat:@"%p", playlist_CurrentPlayingItem( p_playlist )]];
1413 return o_playing_item;
1416 - (NSArray *)draggedItems
1418 return [[o_nodes_array arrayByAddingObjectsFromArray: o_items_array] retain];
1422 @implementation VLCPlaylist (NSOutlineViewDataSource)
1424 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
1426 id o_value = [super outlineView: outlineView child: index ofItem: item];
1428 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p", [o_value pointerValue]]];
1432 /* Required for drag & drop and reordering */
1433 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1435 playlist_t *p_playlist = pl_Get( VLCIntf );
1437 /* First remove the items that were moved during the last drag & drop
1439 [o_items_array removeAllObjects];
1440 [o_nodes_array removeAllObjects];
1442 NSUInteger itemCount = [items count];
1444 for( NSUInteger i = 0 ; i < itemCount ; i++ )
1446 id o_item = [items objectAtIndex: i];
1448 /* Fill the items and nodes to move in 2 different arrays */
1449 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1450 [o_nodes_array addObject: o_item];
1452 [o_items_array addObject: o_item];
1455 /* Now we need to check if there are selected items that are in already
1456 selected nodes. In that case, we only want to move the nodes */
1457 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1458 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1460 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1461 a Drop operation coming from the playlist. */
1463 [pboard declareTypes: [NSArray arrayWithObjects:
1464 @"VLCPlaylistItemPboardType", nil] owner: self];
1465 [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
1470 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
1472 playlist_t *p_playlist = pl_Get( VLCIntf );
1473 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1475 if( !p_playlist ) return NSDragOperationNone;
1477 /* Dropping ON items is not allowed if item is not a node */
1480 if( index == NSOutlineViewDropOnItemIndex &&
1481 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
1483 return NSDragOperationNone;
1487 /* We refuse to drop an item in anything else than a child of the General
1488 Node. We still accept items that would be root nodes of the outlineview
1489 however, to allow drop in an empty playlist. */
1490 if( !( ([self isItem: [item pointerValue] inNode: p_playlist->p_local_category checkItemExistence: NO] ||
1491 ( var_CreateGetBool( p_playlist, "media-library" ) && [self isItem: [item pointerValue] inNode: p_playlist->p_ml_category checkItemExistence: NO] ) ) || item == nil ) )
1493 return NSDragOperationNone;
1496 /* Drop from the Playlist */
1497 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1499 NSUInteger count = [o_nodes_array count];
1500 for( NSUInteger i = 0 ; i < count ; i++ )
1502 /* We refuse to Drop in a child of an item we are moving */
1503 if( [self isItem: [item pointerValue] inNode:
1504 [[o_nodes_array objectAtIndex: i] pointerValue]
1505 checkItemExistence: NO] )
1507 return NSDragOperationNone;
1510 return NSDragOperationMove;
1513 /* Drop from the Finder */
1514 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1516 return NSDragOperationGeneric;
1518 return NSDragOperationNone;
1521 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
1523 playlist_t * p_playlist = pl_Get( VLCIntf );
1524 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1526 /* Drag & Drop inside the playlist */
1527 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1529 int i_row, i_removed_from_node = 0;
1530 playlist_item_t *p_new_parent, *p_item = NULL;
1531 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray: o_items_array];
1532 /* If the item is to be dropped as root item of the outline, make it a
1533 child of the respective general node, if is either the pl or the ml
1534 Else, choose the proposed parent as parent. */
1537 if ([self currentPlaylistRoot] == p_playlist->p_local_category || [self currentPlaylistRoot] == p_playlist->p_ml_category)
1538 p_new_parent = [self currentPlaylistRoot];
1543 p_new_parent = [item pointerValue];
1545 /* Make sure the proposed parent is a node.
1546 (This should never be true) */
1547 if( p_new_parent->i_children < 0 )
1552 NSUInteger count = [o_all_items count];
1553 for( NSUInteger i = 0; i < count; i++ )
1555 playlist_item_t *p_old_parent = NULL;
1556 int i_old_index = 0;
1558 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1559 p_old_parent = p_item->p_parent;
1562 /* We may need the old index later */
1563 if( p_new_parent == p_old_parent )
1565 for( NSInteger j = 0; j < p_old_parent->i_children; j++ )
1567 if( p_old_parent->pp_children[j] == p_item )
1576 // Actually detach the item from the old position
1577 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1581 /* Calculate the new index */
1584 /* If we move the item in the same node, we need to take into
1585 account that one item will be deleted */
1588 if ((p_new_parent == p_old_parent && i_old_index < index + (int)i) )
1590 i_removed_from_node++;
1592 i_new_index = index + i - i_removed_from_node;
1594 // Reattach the item to the new position
1595 playlist_NodeInsert( p_playlist, p_item, p_new_parent, i_new_index );
1599 [self playlistUpdated];
1600 i_row = [o_outline_view rowForItem:[o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", [[o_all_items objectAtIndex: 0] pointerValue]]]];
1604 i_row = [o_outline_view rowForItem:[o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1607 [o_outline_view deselectAll: self];
1608 [o_outline_view selectRowIndexes:[NSIndexSet indexSetWithIndex:i_row] byExtendingSelection:NO];
1609 [o_outline_view scrollRowToVisible: i_row];
1614 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1616 if ([self currentPlaylistRoot] != p_playlist->p_local_category && [self currentPlaylistRoot] != p_playlist->p_ml_category)
1619 playlist_item_t *p_node = [item pointerValue];
1621 NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType]
1622 sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1623 NSUInteger count = [o_values count];
1624 NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
1625 input_thread_t * p_input = pl_CurrentInput( VLCIntf );
1626 BOOL b_returned = NO;
1628 if (count == 1 && p_input)
1630 b_returned = input_AddSubtitle( p_input, make_URI([[o_values objectAtIndex:0] UTF8String], NULL), true );
1631 vlc_object_release( p_input );
1636 vlc_object_release( p_input );
1638 for( NSUInteger i = 0; i < count; i++)
1640 NSDictionary *o_dic;
1641 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
1645 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1649 [o_array addObject: o_dic];
1654 [self appendArray:o_array atPos:index enqueue: YES];
1658 assert( p_node->i_children != -1 );
1659 [self appendNodeArray:o_array inNode: p_node atPos:index enqueue:YES];