1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 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>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
53 /*****************************************************************************
54 * VLCPlaylistView implementation
55 *****************************************************************************/
56 @implementation VLCPlaylistView
58 - (NSMenu *)menuForEvent:(NSEvent *)o_event
60 return( [[self delegate] menuForEvent: o_event] );
63 - (void)keyDown:(NSEvent *)o_event
67 if( [[o_event characters] length] )
69 key = [[o_event characters] characterAtIndex: 0];
74 case NSDeleteCharacter:
75 case NSDeleteFunctionKey:
76 case NSDeleteCharFunctionKey:
77 case NSBackspaceCharacter:
78 [[self delegate] deleteItem:self];
81 case NSEnterCharacter:
82 case NSCarriageReturnCharacter:
83 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
88 [super keyDown: o_event];
95 /*****************************************************************************
96 * VLCPlaylist implementation
97 *****************************************************************************/
98 @implementation VLCPlaylist
105 o_outline_dict = [[NSMutableDictionary alloc] init];
106 o_nodes_array = [[NSMutableArray alloc] init];
107 o_items_array = [[NSMutableArray alloc] init];
117 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
119 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
123 i_current_view = VIEW_CATEGORY;
124 playlist_ViewUpdate( p_playlist, i_current_view );
126 [o_outline_view setTarget: self];
127 [o_outline_view setDelegate: self];
128 [o_outline_view setDataSource: self];
130 [o_outline_view setDoubleAction: @selector(playItem:)];
132 [o_outline_view registerForDraggedTypes:
133 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
134 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
136 /* We need to check whether _defaultTableHeaderSortImage exists, since it
137 belongs to an Apple hidden private API, and then can "disapear" at any time*/
139 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
141 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
145 o_ascendingSortingImage = nil;
148 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
150 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
154 o_descendingSortingImage = nil;
157 o_tc_sortColumn = nil;
159 for( i_index = 0; i_index < p_list->i_count; i_index++ )
162 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
164 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
166 /* create the menu entries used in the playlist menu */
167 o_lmi = [[o_mi_services submenu] addItemWithTitle:
168 [NSString stringWithUTF8String:
169 p_parser->psz_longname ? p_parser->psz_longname :
170 ( p_parser->psz_shortname ? p_parser->psz_shortname:
171 p_parser->psz_object_name)]
172 action: @selector(servicesChange:)
174 [o_lmi setTarget: self];
175 [o_lmi setRepresentedObject:
176 [NSString stringWithCString: p_parser->psz_object_name]];
177 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
178 p_parser->psz_object_name ) )
179 [o_lmi setState: NSOnState];
181 /* create the menu entries for the main menu */
182 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
183 [NSString stringWithUTF8String:
184 p_parser->psz_longname ? p_parser->psz_longname :
185 ( p_parser->psz_shortname ? p_parser->psz_shortname:
186 p_parser->psz_object_name)]
187 action: @selector(servicesChange:)
189 [o_lmi setTarget: self];
190 [o_lmi setRepresentedObject:
191 [NSString stringWithCString: p_parser->psz_object_name]];
192 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
193 p_parser->psz_object_name ) )
194 [o_lmi setState: NSOnState];
197 vlc_list_release( p_list );
198 vlc_object_release( p_playlist );
200 /* Change the simple textfield into a searchField if we can... */
202 if( MACOS_VERSION >= 10.3 )
204 NSView *o_parentview = [o_status_field superview];
205 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
206 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
207 [o_better_search_field setDelegate:self];
208 [[NSNotificationCenter defaultCenter] addObserver: self
209 selector: @selector(searchfieldChanged:)
210 name: NSControlTextDidChangeNotification
211 object: o_better_search_field];
213 [o_better_search_field setTarget:self];
214 [o_better_search_field setAction:@selector(searchItem:)];
216 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
217 [o_parentview addSubview:o_better_search_field];
218 [o_search_field setHidden:YES];
222 //[self playlistUpdated];
225 - (void)searchfieldChanged:(NSNotification *)o_notification
227 [o_search_field setStringValue:[[o_notification object] stringValue]];
232 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
233 [o_mi_play setTitle: _NS("Play")];
234 [o_mi_delete setTitle: _NS("Delete")];
235 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
236 [o_mi_selectall setTitle: _NS("Select All")];
237 [o_mi_info setTitle: _NS("Properties")];
238 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
239 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
240 [o_mi_services setTitle: _NS("Services discovery")];
241 [[o_tc_name headerCell] setStringValue:_NS("Name")];
242 [[o_tc_author headerCell] setStringValue:_NS("Author")];
243 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
244 [o_status_field setStringValue: [NSString stringWithFormat:
245 _NS("no items in playlist")]];
247 [o_random_ckb setTitle: _NS("Random")];
249 [o_search_button setTitle: _NS("Search")];
251 [o_search_field setToolTip: _NS("Search in Playlist")];
252 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
253 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
254 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
257 - (NSOutlineView *)outlineView
259 return o_outline_view;
262 - (void)playlistUpdated
266 /* Clear indications of any existing column sorting*/
267 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
269 [o_outline_view setIndicatorImage:nil inTableColumn:
270 [[o_outline_view tableColumns] objectAtIndex:i]];
273 [o_outline_view setHighlightedTableColumn:nil];
274 o_tc_sortColumn = nil;
275 // TODO Find a way to keep the dict size to a minimum
276 //[o_outline_dict removeAllObjects];
277 [o_outline_view reloadData];
280 - (void)playModeUpdated
282 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
284 vlc_value_t val, val2;
286 if( p_playlist == NULL )
291 var_Get( p_playlist, "loop", &val2 );
292 var_Get( p_playlist, "repeat", &val );
293 if( val.b_bool == VLC_TRUE )
295 [o_loop_popup selectItemAtIndex: 1];
297 else if( val2.b_bool == VLC_TRUE )
299 [o_loop_popup selectItemAtIndex: 2];
303 [o_loop_popup selectItemAtIndex: 0];
306 var_Get( p_playlist, "random", &val );
307 [o_random_ckb setState: val.b_bool];
309 vlc_object_release( p_playlist );
312 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
315 for( i = 0 ; i < p_item->i_parents; i++ )
317 if( p_item->pp_parents[i]->i_view == i_current_view )
319 return p_item->pp_parents[i]->p_parent;
325 - (void)updateRowSelection
331 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
333 playlist_item_t *p_item, *p_temp_item;
334 NSMutableArray *o_array = [NSMutableArray array];
336 if( p_playlist == NULL )
339 p_item = p_playlist->status.p_item;
342 vlc_object_release(p_playlist);
346 p_temp_item = p_item;
347 while( p_temp_item->i_parents > 0 )
349 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
351 p_temp_item = [self parentOfItem: p_temp_item];
352 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
354 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
356 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
362 for (j = 0 ; j < [o_array count] - 1 ; j++)
365 if( ( o_item = [o_outline_dict objectForKey:
366 [NSString stringWithFormat: @"%p",
367 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
368 [o_outline_view expandItem: o_item];
372 i_row = [o_outline_view rowForItem:[o_outline_dict
373 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
375 [o_outline_view selectRow: i_row byExtendingSelection: NO];
376 [o_outline_view scrollRowToVisible: i_row];
378 vlc_object_release(p_playlist);
381 /* Check if p_item is a child of p_node recursively. We need to check the item existence first since OSX sometimes tries to redraw items that have been
382 deleted. We don't do it when not required since this verification takes
383 quite a long time on big playlists (yes, pretty hacky). */
384 - (BOOL)isItem: (playlist_item_t *)p_item
385 inNode: (playlist_item_t *)p_node
386 checkItemExistence:(BOOL)b_check
389 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
391 playlist_item_t *p_temp_item = p_item;
393 if( p_playlist == NULL )
398 if( p_node == p_item )
400 vlc_object_release(p_playlist);
404 if( p_node->i_children < 1)
406 vlc_object_release(p_playlist);
413 vlc_mutex_lock( &p_playlist->object_lock );
417 /* Since outlineView: willDisplayCell:... may call this function with
418 p_items that don't exist anymore, first check if the item is still
419 in the playlist. Any cleaner solution welcomed. */
420 for( i = 0; i < p_playlist->i_all_size; i++ )
422 if( p_playlist->pp_all_items[i] == p_item ) break;
423 else if ( i == p_playlist->i_all_size - 1 )
425 vlc_object_release( p_playlist );
426 vlc_mutex_unlock( &p_playlist->object_lock );
432 while( p_temp_item->i_parents > 0 )
434 p_temp_item = [self parentOfItem: p_temp_item];
435 if( p_temp_item == p_node )
437 vlc_mutex_unlock( &p_playlist->object_lock );
438 vlc_object_release( p_playlist );
442 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
444 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
446 if( p_temp_item->pp_parents[i]->p_parent == p_node )
448 vlc_mutex_unlock( &p_playlist->object_lock );
449 vlc_object_release( p_playlist );
454 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
460 vlc_mutex_unlock( &p_playlist->object_lock );
463 vlc_object_release( p_playlist );
467 /* This method is usefull for instance to remove the selected children of an
468 already selected node, for instance */
469 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
472 for( i = 0 ; i < [o_items count] ; i++ )
474 for ( j = 0 ; j < [o_nodes count] ; j++ )
476 if( o_items == o_nodes)
478 if( j == i ) continue;
480 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
481 inNode: [[o_nodes objectAtIndex:j] pointerValue]
482 checkItemExistence: NO] )
484 [o_items removeObjectAtIndex:i];
485 /* We need to execute the next iteration with the same index
486 since the current item has been deleted */
495 - (IBAction)savePlaylist:(id)sender
497 intf_thread_t * p_intf = VLCIntf;
498 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
501 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
502 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
503 [o_save_panel setTitle: _NS("Save Playlist")];
504 [o_save_panel setPrompt: _NS("Save")];
506 if( [o_save_panel runModalForDirectory: nil
507 file: o_name] == NSOKButton )
509 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
514 /* When called retrieves the selected outlineview row and plays that node or item */
515 - (IBAction)playItem:(id)sender
517 intf_thread_t * p_intf = VLCIntf;
518 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
521 if( p_playlist != NULL )
523 playlist_item_t *p_item;
524 playlist_item_t *p_node = NULL;
527 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
531 if( p_item->i_children == -1 )
533 p_node = [self parentOfItem: p_item];
535 /* for( i = 0 ; i < p_item->i_parents ; i++ )
537 if( p_item->pp_parents[i]->i_view == i_current_view )
539 p_node = p_item->pp_parents[i]->p_parent;
546 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
548 p_item = p_node->pp_children[0];
555 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
557 vlc_object_release( p_playlist );
561 - (IBAction)servicesChange:(id)sender
563 NSMenuItem *o_mi = (NSMenuItem *)sender;
564 NSString *o_string = [o_mi representedObject];
565 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
567 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
568 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
570 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
572 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
573 [o_string cString] ) ? YES : NO];
575 i_current_view = VIEW_CATEGORY;
576 playlist_ViewUpdate( p_playlist, i_current_view );
577 vlc_object_release( p_playlist );
578 [self playlistUpdated];
582 - (IBAction)selectAll:(id)sender
584 [o_outline_view selectAll: nil];
587 - (IBAction)deleteItem:(id)sender
589 int i, i_count, i_row;
590 NSMutableArray *o_to_delete;
593 playlist_t * p_playlist;
594 intf_thread_t * p_intf = VLCIntf;
596 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
599 if ( p_playlist == NULL )
603 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
604 i_count = [o_to_delete count];
606 for( i = 0; i < i_count; i++ )
608 o_number = [o_to_delete lastObject];
609 i_row = [o_number intValue];
610 id o_item = [o_outline_view itemAtRow: i_row];
611 playlist_item_t *p_item = [o_item pointerValue];
612 [o_to_delete removeObject: o_number];
613 [o_outline_view deselectRow: i_row];
615 if( [[o_outline_view dataSource] outlineView:o_outline_view
616 numberOfChildrenOfItem: o_item] > 0 )
617 //is a node and not an item
619 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
620 [self isItem: p_playlist->status.p_item inNode:
621 ((playlist_item_t *)[o_item pointerValue])
622 checkItemExistence: NO] == YES )
624 // if current item is in selected node and is playing then stop playlist
625 playlist_Stop( p_playlist );
627 vlc_mutex_lock( &p_playlist->object_lock );
628 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
629 vlc_mutex_unlock( &p_playlist->object_lock );
633 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
634 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
636 playlist_Stop( p_playlist );
638 vlc_mutex_lock( &p_playlist->object_lock );
639 playlist_Delete( p_playlist, p_item->input.i_id );
640 vlc_mutex_unlock( &p_playlist->object_lock );
643 [self playlistUpdated];
644 vlc_object_release( p_playlist );
647 - (IBAction)sortNodeByName:(id)sender
649 [self sortNode: SORT_TITLE];
652 - (IBAction)sortNodeByAuthor:(id)sender
654 [self sortNode: SORT_AUTHOR];
657 - (void)sortNode:(int)i_mode
659 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
661 playlist_item_t * p_item;
663 if (p_playlist == NULL)
668 if( [o_outline_view selectedRow] > -1 )
670 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
674 /*If no item is selected, sort the whole playlist*/
676 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
677 p_item = p_view->p_root;
680 if( p_item->i_children > -1 ) // the item is a node
682 vlc_mutex_lock( &p_playlist->object_lock );
683 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
684 vlc_mutex_unlock( &p_playlist->object_lock );
690 for( i = 0 ; i < p_item->i_parents ; i++ )
692 if( p_item->pp_parents[i]->i_view == i_current_view )
694 vlc_mutex_lock( &p_playlist->object_lock );
695 playlist_RecursiveNodeSort( p_playlist,
696 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
697 vlc_mutex_unlock( &p_playlist->object_lock );
702 vlc_object_release( p_playlist );
703 [self playlistUpdated];
706 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
708 intf_thread_t * p_intf = VLCIntf;
709 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
712 if( p_playlist == NULL )
716 playlist_item_t *p_item;
718 BOOL b_rem = FALSE, b_dir = FALSE;
719 NSString *o_uri, *o_name;
724 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
725 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
726 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
728 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
729 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
731 int i_count, i_index;
732 struct statfs *mounts = NULL;
734 i_count = getmntinfo (&mounts, MNT_NOWAIT);
735 /* getmntinfo returns a pointer to static data. Do not free. */
736 for( i_index = 0 ; i_index < i_count; i_index++ )
738 NSMutableString *o_temp, *o_temp2;
739 o_temp = [NSMutableString stringWithString: o_uri];
740 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
741 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
742 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
743 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
745 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
747 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
751 /* If no name, then make a guess */
752 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
754 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
755 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
756 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
758 /* All of this is to make sure CD's play when you D&D them on VLC */
759 /* Converts mountpoint to a /dev file */
762 NSMutableString *o_temp;
764 buf = (struct statfs *) malloc (sizeof(struct statfs));
765 statfs( [o_uri fileSystemRepresentation], buf );
766 psz_dev = strdup(buf->f_mntfromname);
767 o_temp = [NSMutableString stringWithCString: psz_dev ];
768 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
769 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
770 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
774 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
780 for( i = 0; i < (int)[o_options count]; i++ )
782 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
786 /* Recent documents menu */
787 o_true_file = [NSURL fileURLWithPath: o_uri];
788 if( o_true_file != nil )
790 [[NSDocumentController sharedDocumentController]
791 noteNewRecentDocumentURL: o_true_file];
794 vlc_object_release( p_playlist );
798 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
801 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
803 if( p_playlist == NULL )
808 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
810 playlist_item_t *p_item;
811 NSDictionary *o_one_item;
814 o_one_item = [o_array objectAtIndex: i_item];
815 p_item = [self createItem: o_one_item];
822 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
824 if( i_item == 0 && !b_enqueue )
826 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
829 vlc_object_release( p_playlist );
832 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
835 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
837 if( p_playlist == NULL )
842 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
844 playlist_item_t *p_item;
845 NSDictionary *o_one_item;
848 o_one_item = [o_array objectAtIndex: i_item];
849 p_item = [self createItem: o_one_item];
856 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
858 if( i_item == 0 && !b_enqueue )
860 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
863 vlc_object_release( p_playlist );
867 - (IBAction)handlePopUp:(id)sender
870 intf_thread_t * p_intf = VLCIntf;
871 vlc_value_t val1,val2;
872 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
874 if( p_playlist == NULL )
879 switch( [o_loop_popup indexOfSelectedItem] )
884 var_Set( p_playlist, "loop", val1 );
886 var_Set( p_playlist, "repeat", val1 );
887 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
892 var_Set( p_playlist, "repeat", val1 );
894 var_Set( p_playlist, "loop", val1 );
895 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
899 var_Get( p_playlist, "repeat", &val1 );
900 var_Get( p_playlist, "loop", &val2 );
901 if( val1.b_bool || val2.b_bool )
904 var_Set( p_playlist, "repeat", val1 );
905 var_Set( p_playlist, "loop", val1 );
906 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
910 vlc_object_release( p_playlist );
911 [self playlistUpdated];
914 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
916 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
918 playlist_item_t *p_selected_item;
919 int i_current, i_selected_row;
924 i_selected_row = [o_outline_view selectedRow];
925 if (i_selected_row < 0)
928 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
929 i_selected_row] pointerValue];
931 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
934 NSString *o_current_name, *o_current_author;
936 vlc_mutex_lock( &p_playlist->object_lock );
937 o_current_name = [NSString stringWithUTF8String:
938 p_item->pp_children[i_current]->input.psz_name];
939 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
940 _("Meta-information"),_("Artist") );
941 o_current_author = [NSString stringWithUTF8String: psz_temp];
943 vlc_mutex_unlock( &p_playlist->object_lock );
945 if( p_selected_item == p_item->pp_children[i_current] &&
946 b_selected_item_met == NO )
948 b_selected_item_met = YES;
950 else if( p_selected_item == p_item->pp_children[i_current] &&
951 b_selected_item_met == YES )
953 vlc_object_release( p_playlist );
956 else if( b_selected_item_met == YES &&
957 ( [o_current_name rangeOfString:[o_search_field
958 stringValue] options:NSCaseInsensitiveSearch ].length ||
959 [o_current_author rangeOfString:[o_search_field
960 stringValue] options:NSCaseInsensitiveSearch ].length ) )
962 vlc_object_release( p_playlist );
963 /*Adds the parent items in the result array as well, so that we can
965 return [NSMutableArray arrayWithObject: [NSValue
966 valueWithPointer: p_item->pp_children[i_current]]];
968 if( p_item->pp_children[i_current]->i_children > 0 )
970 id o_result = [self subSearchItem:
971 p_item->pp_children[i_current]];
972 if( o_result != NULL )
974 vlc_object_release( p_playlist );
975 [o_result insertObject: [NSValue valueWithPointer:
976 p_item->pp_children[i_current]] atIndex:0];
981 vlc_object_release( p_playlist );
985 - (IBAction)searchItem:(id)sender
987 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
989 playlist_view_t * p_view;
995 b_selected_item_met = NO;
997 if( p_playlist == NULL )
999 p_view = playlist_ViewFind( p_playlist, i_current_view );
1003 /*First, only search after the selected item:*
1004 *(b_selected_item_met = NO) */
1005 o_result = [self subSearchItem:p_view->p_root];
1006 if( o_result == NULL )
1008 /* If the first search failed, search again from the beginning */
1009 o_result = [self subSearchItem:p_view->p_root];
1011 if( o_result != NULL )
1014 if( [[o_result objectAtIndex: 0] pointerValue] ==
1015 p_playlist->p_general )
1020 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1022 [o_outline_view expandItem: [o_outline_dict objectForKey:
1023 [NSString stringWithFormat: @"%p",
1024 [[o_result objectAtIndex: i] pointerValue]]]];
1026 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1027 [NSString stringWithFormat: @"%p",
1028 [[o_result objectAtIndex: [o_result count] - 1 ]
1033 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1034 [o_outline_view scrollRowToVisible: i_row];
1037 vlc_object_release( p_playlist );
1040 - (IBAction)recursiveExpandNode:(id)sender
1043 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1044 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1046 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1047 isItemExpandable: o_item] )
1049 for( i = 0 ; i < p_item->i_parents ; i++ )
1051 if( p_item->pp_parents[i]->i_view == i_current_view )
1053 o_item = [o_outline_dict objectForKey: [NSString
1054 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1060 /* We need to collapse the node first, since OSX refuses to recursively
1061 expand an already expanded node, even if children nodes are collapsed. */
1062 [o_outline_view collapseItem: o_item collapseChildren: YES];
1063 [o_outline_view expandItem: o_item expandChildren: YES];
1066 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1070 vlc_bool_t b_item_sel;
1072 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1074 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1075 [o_outline_view selectedRow] != -1 );
1076 b_rows = [o_outline_view numberOfRows] != 0;
1078 [o_mi_play setEnabled: b_item_sel];
1079 [o_mi_delete setEnabled: b_item_sel];
1080 [o_mi_selectall setEnabled: b_rows];
1081 [o_mi_info setEnabled: b_item_sel];
1082 [o_mi_recursive_expand setEnabled: b_item_sel];
1083 [o_mi_sort_name setEnabled: b_item_sel];
1084 [o_mi_sort_author setEnabled: b_item_sel];
1086 return( o_ctx_menu );
1089 - (playlist_item_t *)selectedPlaylistItem
1091 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
1095 - (void)outlineView: (NSTableView*)o_tv
1096 didClickTableColumn:(NSTableColumn *)o_tc
1098 int i_mode = 0, i_type;
1099 intf_thread_t *p_intf = VLCIntf;
1100 playlist_view_t *p_view;
1102 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1104 if( p_playlist == NULL )
1109 /* Check whether the selected table column header corresponds to a
1110 sortable table column*/
1111 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1113 vlc_object_release( p_playlist );
1117 p_view = playlist_ViewFind( p_playlist, i_current_view );
1119 if( o_tc_sortColumn == o_tc )
1121 b_isSortDescending = !b_isSortDescending;
1125 b_isSortDescending = VLC_FALSE;
1128 if( o_tc == o_tc_name )
1130 i_mode = SORT_TITLE;
1132 else if( o_tc == o_tc_author )
1134 i_mode = SORT_AUTHOR;
1137 if( b_isSortDescending )
1139 i_type = ORDER_REVERSE;
1143 i_type = ORDER_NORMAL;
1146 vlc_mutex_lock( &p_playlist->object_lock );
1147 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1148 vlc_mutex_unlock( &p_playlist->object_lock );
1150 vlc_object_release( p_playlist );
1151 [self playlistUpdated];
1153 o_tc_sortColumn = o_tc;
1154 [o_outline_view setHighlightedTableColumn:o_tc];
1156 if( b_isSortDescending )
1158 [o_outline_view setIndicatorImage:o_descendingSortingImage
1159 inTableColumn:o_tc];
1163 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1164 inTableColumn:o_tc];
1169 - (void)outlineView:(NSOutlineView *)outlineView
1170 willDisplayCell:(id)cell
1171 forTableColumn:(NSTableColumn *)tableColumn
1174 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1179 if( !p_playlist ) return;
1181 o_playing_item = [o_outline_dict objectForKey:
1182 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1184 if( [self isItem: [o_playing_item pointerValue] inNode:
1185 [item pointerValue] checkItemExistence: YES]
1186 || [o_playing_item isEqual: item] )
1188 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1192 [cell setFont: [NSFont systemFontOfSize: 0]];
1194 vlc_object_release( p_playlist );
1199 @implementation VLCPlaylist (NSOutlineViewDataSource)
1201 /* return the number of children for Obj-C pointer item */ /* DONE */
1202 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
1205 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1207 if( p_playlist == NULL || outlineView != o_outline_view )
1213 playlist_view_t *p_view;
1214 p_view = playlist_ViewFind( p_playlist, i_current_view );
1215 if( p_view && p_view->p_root )
1217 i_return = p_view->p_root->i_children;
1218 if( i_current_view == VIEW_CATEGORY )
1220 i_return--; /* remove the GENERAL item from the list */
1221 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
1227 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
1229 i_return = p_item->i_children;
1231 vlc_object_release( p_playlist );
1239 /* return the child at index for the Obj-C pointer item */ /* DONE */
1240 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1242 playlist_item_t *p_return = NULL;
1243 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1247 if( p_playlist == NULL )
1253 playlist_view_t *p_view;
1254 p_view = playlist_ViewFind( p_playlist, i_current_view );
1255 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
1257 if( i_current_view == VIEW_CATEGORY )
1259 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
1261 p_return = p_playlist->p_general->pp_children[index];
1263 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
1265 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
1272 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
1273 if( p_item && index < p_item->i_children && index >= 0 )
1274 p_return = p_item->pp_children[index];
1277 if( p_playlist->i_size >= 2 )
1279 [o_status_field setStringValue: [NSString stringWithFormat:
1280 _NS("%i items in playlist"), p_playlist->i_size]];
1284 if( p_playlist->i_size == 0 )
1286 [o_status_field setStringValue: [NSString stringWithFormat:
1287 _NS("no items in playlist"), p_playlist->i_size]];
1291 [o_status_field setStringValue: [NSString stringWithFormat:
1292 _NS("1 item in playlist"), p_playlist->i_size]];
1296 vlc_object_release( p_playlist );
1298 o_value = [[NSValue valueWithPointer: p_return] retain];
1300 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p", p_return]];
1304 /* is the item expandable */
1305 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
1308 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1310 if( p_playlist == NULL )
1316 playlist_view_t *p_view;
1317 p_view = playlist_ViewFind( p_playlist, i_current_view );
1318 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
1320 if( i_current_view == VIEW_CATEGORY )
1323 i_return += p_playlist->p_general->i_children;
1328 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
1330 i_return = p_item->i_children;
1332 vlc_object_release( p_playlist );
1340 /* retrieve the string values for the cells */
1341 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
1344 intf_thread_t *p_intf = VLCIntf;
1345 playlist_t *p_playlist;
1346 playlist_item_t *p_item;
1348 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
1350 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1352 if( p_playlist == NULL )
1357 p_item = (playlist_item_t *)[item pointerValue];
1359 if( p_item == NULL )
1361 vlc_object_release( p_playlist );
1365 if( [[o_tc identifier] isEqualToString:@"1"] )
1367 o_value = [NSString stringWithUTF8String:
1368 p_item->input.psz_name];
1369 if( o_value == NULL )
1370 o_value = [NSString stringWithCString:
1371 p_item->input.psz_name];
1373 else if( [[o_tc identifier] isEqualToString:@"2"] )
1376 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
1378 if( psz_temp == NULL )
1382 o_value = [NSString stringWithUTF8String: psz_temp];
1383 if( o_value == NULL )
1385 o_value = [NSString stringWithCString: psz_temp];
1390 else if( [[o_tc identifier] isEqualToString:@"3"] )
1392 char psz_duration[MSTRTIME_MAX_SIZE];
1393 mtime_t dur = p_item->input.i_duration;
1396 secstotimestr( psz_duration, dur/1000000 );
1397 o_value = [NSString stringWithUTF8String: psz_duration];
1401 o_value = @"-:--:--";
1404 vlc_object_release( p_playlist );
1409 /* Required for drag & drop and reordering */
1410 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1413 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1416 /* First remove the items that were moved during the last drag & drop
1418 [o_items_array removeAllObjects];
1419 [o_nodes_array removeAllObjects];
1421 if( !p_playlist ) return NO;
1423 for( i = 0 ; i < [items count] ; i++ )
1425 id o_item = [items objectAtIndex: i];
1427 /* Refuse to move items that are not in the General Node
1428 (Service Discovery) */
1429 if( ![self isItem: [o_item pointerValue] inNode:
1430 p_playlist->p_general checkItemExistence: NO])
1432 vlc_object_release(p_playlist);
1435 /* Fill the items and nodes to move in 2 different arrays */
1436 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1437 [o_nodes_array addObject: o_item];
1439 [o_items_array addObject: o_item];
1442 /* Now we need to check if there are selected items that are in already
1443 selected nodes. In that case, we only want to move the nodes */
1444 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1445 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1448 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1450 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1452 if( j == i ) continue;
1453 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1454 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1456 [o_nodes_array removeObjectAtIndex:i];
1457 /* We need to execute the next iteration with the same index
1458 since the current item has been deleted */
1465 for( i = 0 ; i < [o_items_array count] ; i++ )
1467 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1469 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1470 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1472 [o_items_array removeObjectAtIndex:i];
1479 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1480 a Drop operation comçing from the playlist.
1481 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1484 [pboard declareTypes: [NSArray arrayWithObjects:
1485 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1486 [pboard setPropertyList:[NSArray array]
1487 forType:NSFilenamesPboardType];
1489 vlc_object_release(p_playlist);
1493 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1495 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1497 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1499 if( !p_playlist ) return NSDragOperationNone;
1501 /* We refuse to drop an item in anything else than a child of the General
1502 Node. We still accept items that would be root nodes of the outlineview
1503 however, to allow drop in an empty playlist.*/
1504 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1505 checkItemExistence: NO] || item == nil) )
1507 vlc_object_release(p_playlist);
1508 return NSDragOperationNone;
1511 /* Drop from the Playlist */
1512 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1515 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1517 /* We refuse to Drop in a child of an item we are moving */
1518 if( [self isItem: [item pointerValue] inNode:
1519 [[o_nodes_array objectAtIndex: i] pointerValue]
1520 checkItemExistence: NO] )
1522 vlc_object_release(p_playlist);
1523 return NSDragOperationNone;
1526 vlc_object_release(p_playlist);
1527 return NSDragOperationMove;
1530 /* Drop from the Finder */
1531 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1533 vlc_object_release(p_playlist);
1534 return NSDragOperationGeneric;
1536 vlc_object_release(p_playlist);
1537 return NSDragOperationNone;
1540 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1542 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1544 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1546 if( !p_playlist ) return NO;
1548 /* Drag & Drop inside the playlist */
1549 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1553 playlist_item_t *p_new_parent, *p_item = NULL;
1554 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1556 /* If the item is to be dropped as root item of the outline, make it a
1557 child of the General node.
1558 Else, choose the proposed parent as parent. */
1560 p_new_parent = p_playlist->p_general;
1562 p_new_parent = [item pointerValue];
1564 /* If the proposed parent is not a node, then use the parent node of
1566 if( p_new_parent->i_children <= 0 )
1569 playlist_item_t *p_temp_item = p_new_parent;
1570 p_new_parent = [self parentOfItem: p_new_parent];
1573 vlc_object_release(p_playlist);
1576 /* Calculate the position of the dropped item in this new parent:
1577 following the first proposed parent. */
1578 for( j = 0; j < p_new_parent->i_children; j++ )
1580 if( p_new_parent->pp_children[j] == p_temp_item )
1585 else if( j == p_new_parent->i_children - 1 )
1590 for( i = 0; i < [o_all_items count]; i++ )
1592 playlist_item_t *p_old_parent = NULL;
1593 int i_old_index = 0;
1595 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1596 p_old_parent = [self parentOfItem: p_item];
1599 /* We may need the old index later */
1600 if( p_new_parent == p_old_parent )
1603 for( j = 0; j < p_old_parent->i_children; j++ )
1605 if( p_old_parent->pp_children[j] == p_item )
1614 /* If we move the playing item in a different node or we move the
1615 node containing the playing item in a different node, then stop
1616 playback, or the playlist refuses to detach the item. */
1617 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1618 (( p_item == p_playlist->status.p_item &&
1619 p_new_parent != p_old_parent) ||
1620 ( p_item->i_children > 0 &&
1621 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1623 playlist_Stop( p_playlist );
1625 vlc_mutex_lock( &p_playlist->object_lock );
1626 // Acually detach the item from the old position
1627 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1629 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1633 /* Calculate the new index */
1636 /* If we move the item in the same node, we need to take into
1637 account that one item will be deleted */
1638 else if((p_new_parent == p_old_parent &&
1639 i_old_index < index + (int)i)
1640 || p_new_parent == p_playlist->p_general || index == 0 )
1641 i_new_index = index + i;
1643 i_new_index = index + i + 1;
1644 // Reattach the item to the new position
1645 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1646 p_new_parent, i_new_index );
1648 vlc_mutex_unlock( &p_playlist->object_lock );
1650 [self playlistUpdated];
1651 i_row = [o_outline_view rowForItem:[o_outline_dict
1652 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
1656 i_row = [o_outline_view rowForItem:[o_outline_dict
1657 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1660 [o_outline_view deselectAll: self];
1661 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1662 [o_outline_view scrollRowToVisible: i_row];
1664 vlc_object_release(p_playlist);
1668 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1671 playlist_item_t *p_node = [item pointerValue];
1673 NSArray *o_array = [NSArray array];
1674 NSArray *o_values = [[o_pasteboard propertyListForType:
1675 NSFilenamesPboardType]
1676 sortedArrayUsingSelector:
1677 @selector(caseInsensitiveCompare:)];
1679 for( i = 0; i < (int)[o_values count]; i++)
1681 NSDictionary *o_dic;
1682 o_dic = [NSDictionary dictionaryWithObject:[o_values
1683 objectAtIndex:i] forKey:@"ITEM_URL"];
1684 o_array = [o_array arrayByAddingObject: o_dic];
1689 [self appendArray: o_array atPos: index enqueue: YES];
1691 else if( p_node->i_children == -1 )
1694 playlist_item_t *p_real_node = NULL;
1696 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1698 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1700 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1703 if( i_counter == p_node->i_parents )
1705 vlc_object_release(p_playlist);
1709 [self appendNodeArray: o_array inNode: p_real_node
1710 atPos: index inView: i_current_view enqueue: YES];
1714 [self appendNodeArray: o_array inNode: p_node
1715 atPos: index inView: i_current_view enqueue: YES];
1717 vlc_object_release( p_playlist );
1720 vlc_object_release( p_playlist );
1724 /* Delegate method of NSWindow */
1725 /*- (void)windowWillClose:(NSNotification *)aNotification
1727 [o_btn_playlist setState: NSOffState];