1 /*****************************************************************************
2 * playlist.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2002-2005 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videola/n dot org>
9 * Benjamin Pracht <bigben at videolab dot org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
27 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
29 * create toggle buttons for the shuffle, repeat one, repeat all functions.
30 * implement drag and drop and item reordering.
31 * reimplement enable/disable item
32 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
33 (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
37 /*****************************************************************************
39 *****************************************************************************/
40 #include <stdlib.h> /* malloc(), free() */
41 #include <sys/param.h> /* for MAXPATHLEN */
44 #include <sys/mount.h>
54 /*****************************************************************************
55 * VLCPlaylistView implementation
56 *****************************************************************************/
57 @implementation VLCPlaylistView
59 - (NSMenu *)menuForEvent:(NSEvent *)o_event
61 return( [[self delegate] menuForEvent: o_event] );
64 - (void)keyDown:(NSEvent *)o_event
68 if( [[o_event characters] length] )
70 key = [[o_event characters] characterAtIndex: 0];
75 case NSDeleteCharacter:
76 case NSDeleteFunctionKey:
77 case NSDeleteCharFunctionKey:
78 case NSBackspaceCharacter:
79 [[self delegate] deleteItem:self];
82 case NSEnterCharacter:
83 case NSCarriageReturnCharacter:
84 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
89 [super keyDown: o_event];
97 /*****************************************************************************
98 * VLCPlaylistCommon implementation
100 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
101 * It contains the common methods and elements of these 2 entities.
102 *****************************************************************************/
103 @implementation VLCPlaylistCommon
107 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
109 i_current_view = VIEW_CATEGORY;
110 playlist_ViewUpdate( p_playlist, i_current_view );
112 [o_outline_view setTarget: self];
113 [o_outline_view setDelegate: self];
114 [o_outline_view setDataSource: self];
116 vlc_object_release( p_playlist );
122 [[o_tc_name headerCell] setStringValue:_NS("Name")];
123 [[o_tc_author headerCell] setStringValue:_NS("Author")];
124 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
127 - (NSOutlineView *)outlineView
129 return o_outline_view;
132 - (playlist_item_t *)selectedPlaylistItem
134 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
140 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
142 /* return the number of children for Obj-C pointer item */ /* DONE */
143 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
146 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
148 if( p_playlist == NULL || outlineView != o_outline_view )
154 playlist_view_t *p_view;
155 p_view = playlist_ViewFind( p_playlist, i_current_view );
156 if( p_view && p_view->p_root )
158 i_return = p_view->p_root->i_children;
159 if( i_current_view == VIEW_CATEGORY )
161 i_return--; /* remove the GENERAL item from the list */
162 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
168 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
170 i_return = p_item->i_children;
172 vlc_object_release( p_playlist );
180 /* return the child at index for the Obj-C pointer item */ /* DONE */
181 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
183 playlist_item_t *p_return = NULL;
184 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
188 if( p_playlist == NULL )
194 playlist_view_t *p_view;
195 p_view = playlist_ViewFind( p_playlist, i_current_view );
196 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
198 if( i_current_view == VIEW_CATEGORY )
200 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
202 p_return = p_playlist->p_general->pp_children[index];
204 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
206 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
212 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
213 if( p_item && index < p_item->i_children && index >= 0 )
214 p_return = p_item->pp_children[index];
218 vlc_object_release( p_playlist );
220 o_value = [[NSValue valueWithPointer: p_return] retain];
225 /* is the item expandable */
226 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
229 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
231 if( p_playlist == NULL )
237 playlist_view_t *p_view;
238 p_view = playlist_ViewFind( p_playlist, i_current_view );
239 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
241 if( i_current_view == VIEW_CATEGORY )
244 i_return += p_playlist->p_general->i_children;
249 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
251 i_return = p_item->i_children;
253 vlc_object_release( p_playlist );
261 /* retrieve the string values for the cells */
262 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
265 intf_thread_t *p_intf = VLCIntf;
266 playlist_t *p_playlist;
267 playlist_item_t *p_item;
269 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
271 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
273 if( p_playlist == NULL )
278 p_item = (playlist_item_t *)[item pointerValue];
282 vlc_object_release( p_playlist );
286 if( [[o_tc identifier] isEqualToString:@"1"] )
288 o_value = [NSString stringWithUTF8String:
289 p_item->input.psz_name];
290 if( o_value == NULL )
291 o_value = [NSString stringWithCString:
292 p_item->input.psz_name];
294 else if( [[o_tc identifier] isEqualToString:@"2"] )
297 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
299 if( psz_temp == NULL )
303 o_value = [NSString stringWithUTF8String: psz_temp];
304 if( o_value == NULL )
306 o_value = [NSString stringWithCString: psz_temp];
311 else if( [[o_tc identifier] isEqualToString:@"3"] )
313 char psz_duration[MSTRTIME_MAX_SIZE];
314 mtime_t dur = p_item->input.i_duration;
317 secstotimestr( psz_duration, dur/1000000 );
318 o_value = [NSString stringWithUTF8String: psz_duration];
322 o_value = @"-:--:--";
325 vlc_object_release( p_playlist );
332 /*****************************************************************************
333 * VLCPlaylistWizard implementation
334 *****************************************************************************/
335 @implementation VLCPlaylistWizard
337 - (IBAction)reloadOutlineView
339 /* Only reload the outlineview if the wizard window is open since this can
340 be quite long on big playlists */
341 if( [[o_outline_view window] isVisible] )
343 [o_outline_view reloadData];
349 /*****************************************************************************
350 * VLCPlaylist implementation
351 *****************************************************************************/
352 @implementation VLCPlaylist
359 o_outline_dict = [[NSMutableDictionary alloc] init];
360 o_nodes_array = [[NSMutableArray alloc] init];
361 o_items_array = [[NSMutableArray alloc] init];
371 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
373 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
378 [super awakeFromNib];
380 [o_outline_view setDoubleAction: @selector(playItem:)];
382 [o_outline_view registerForDraggedTypes:
383 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
384 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
386 /* We need to check whether _defaultTableHeaderSortImage exists, since it
387 belongs to an Apple hidden private API, and then can "disapear" at any time*/
389 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
391 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
395 o_ascendingSortingImage = nil;
398 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
400 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
404 o_descendingSortingImage = nil;
407 o_tc_sortColumn = nil;
409 for( i_index = 0; i_index < p_list->i_count; i_index++ )
412 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
414 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
416 /* create the menu entries used in the playlist menu */
417 o_lmi = [[o_mi_services submenu] addItemWithTitle:
418 [NSString stringWithUTF8String:
419 p_parser->psz_longname ? p_parser->psz_longname :
420 ( p_parser->psz_shortname ? p_parser->psz_shortname:
421 p_parser->psz_object_name)]
422 action: @selector(servicesChange:)
424 [o_lmi setTarget: self];
425 [o_lmi setRepresentedObject:
426 [NSString stringWithCString: p_parser->psz_object_name]];
427 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
428 p_parser->psz_object_name ) )
429 [o_lmi setState: NSOnState];
431 /* create the menu entries for the main menu */
432 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
433 [NSString stringWithUTF8String:
434 p_parser->psz_longname ? p_parser->psz_longname :
435 ( p_parser->psz_shortname ? p_parser->psz_shortname:
436 p_parser->psz_object_name)]
437 action: @selector(servicesChange:)
439 [o_lmi setTarget: self];
440 [o_lmi setRepresentedObject:
441 [NSString stringWithCString: p_parser->psz_object_name]];
442 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
443 p_parser->psz_object_name ) )
444 [o_lmi setState: NSOnState];
447 vlc_list_release( p_list );
448 vlc_object_release( p_playlist );
450 /* Change the simple textfield into a searchField if we can... */
452 if( MACOS_VERSION >= 10.3 )
454 NSView *o_parentview = [o_status_field superview];
455 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
456 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
457 [o_better_search_field setDelegate:self];
458 [[NSNotificationCenter defaultCenter] addObserver: self
459 selector: @selector(searchfieldChanged:)
460 name: NSControlTextDidChangeNotification
461 object: o_better_search_field];
463 [o_better_search_field setTarget:self];
464 [o_better_search_field setAction:@selector(searchItem:)];
466 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
467 [o_parentview addSubview:o_better_search_field];
468 [o_search_field setHidden:YES];
471 //[self playlistUpdated];
474 - (void)searchfieldChanged:(NSNotification *)o_notification
476 [o_search_field setStringValue:[[o_notification object] stringValue]];
483 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
484 [o_mi_play setTitle: _NS("Play")];
485 [o_mi_delete setTitle: _NS("Delete")];
486 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
487 [o_mi_selectall setTitle: _NS("Select All")];
488 [o_mi_info setTitle: _NS("Properties")];
489 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
490 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
491 [o_mi_services setTitle: _NS("Services discovery")];
492 [o_status_field setStringValue: [NSString stringWithFormat:
493 _NS("no items in playlist")]];
495 [o_random_ckb setTitle: _NS("Random")];
497 [o_search_button setTitle: _NS("Search")];
499 [o_search_field setToolTip: _NS("Search in Playlist")];
500 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
501 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
502 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
505 - (void)playlistUpdated
509 /* Clear indications of any existing column sorting*/
510 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
512 [o_outline_view setIndicatorImage:nil inTableColumn:
513 [[o_outline_view tableColumns] objectAtIndex:i]];
516 [o_outline_view setHighlightedTableColumn:nil];
517 o_tc_sortColumn = nil;
518 // TODO Find a way to keep the dict size to a minimum
519 //[o_outline_dict removeAllObjects];
520 [o_outline_view reloadData];
521 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
524 - (void)playModeUpdated
526 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
528 vlc_value_t val, val2;
530 if( p_playlist == NULL )
535 var_Get( p_playlist, "loop", &val2 );
536 var_Get( p_playlist, "repeat", &val );
537 if( val.b_bool == VLC_TRUE )
539 [o_loop_popup selectItemAtIndex: 1];
541 else if( val2.b_bool == VLC_TRUE )
543 [o_loop_popup selectItemAtIndex: 2];
547 [o_loop_popup selectItemAtIndex: 0];
550 var_Get( p_playlist, "random", &val );
551 [o_random_ckb setState: val.b_bool];
553 vlc_object_release( p_playlist );
556 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
559 for( i = 0 ; i < p_item->i_parents; i++ )
561 if( p_item->pp_parents[i]->i_view == i_current_view )
563 return p_item->pp_parents[i]->p_parent;
569 - (void)updateRowSelection
575 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
577 playlist_item_t *p_item, *p_temp_item;
578 NSMutableArray *o_array = [NSMutableArray array];
580 if( p_playlist == NULL )
583 p_item = p_playlist->status.p_item;
586 vlc_object_release(p_playlist);
590 p_temp_item = p_item;
591 while( p_temp_item->i_parents > 0 )
593 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
595 p_temp_item = [self parentOfItem: p_temp_item];
596 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
598 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
600 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
606 for (j = 0 ; j < [o_array count] - 1 ; j++)
609 if( ( o_item = [o_outline_dict objectForKey:
610 [NSString stringWithFormat: @"%p",
611 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
612 [o_outline_view expandItem: o_item];
616 i_row = [o_outline_view rowForItem:[o_outline_dict
617 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
619 [o_outline_view selectRow: i_row byExtendingSelection: NO];
620 [o_outline_view scrollRowToVisible: i_row];
622 vlc_object_release(p_playlist);
625 /* 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
626 deleted. We don't do it when not required since this verification takes
627 quite a long time on big playlists (yes, pretty hacky). */
628 - (BOOL)isItem: (playlist_item_t *)p_item
629 inNode: (playlist_item_t *)p_node
630 checkItemExistence:(BOOL)b_check
633 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
635 playlist_item_t *p_temp_item = p_item;
637 if( p_playlist == NULL )
642 if( p_node == p_item )
644 vlc_object_release(p_playlist);
648 if( p_node->i_children < 1)
650 vlc_object_release(p_playlist);
657 vlc_mutex_lock( &p_playlist->object_lock );
661 /* Since outlineView: willDisplayCell:... may call this function with
662 p_items that don't exist anymore, first check if the item is still
663 in the playlist. Any cleaner solution welcomed. */
664 for( i = 0; i < p_playlist->i_all_size; i++ )
666 if( p_playlist->pp_all_items[i] == p_item ) break;
667 else if ( i == p_playlist->i_all_size - 1 )
669 vlc_object_release( p_playlist );
670 vlc_mutex_unlock( &p_playlist->object_lock );
676 while( p_temp_item->i_parents > 0 )
678 p_temp_item = [self parentOfItem: p_temp_item];
679 if( p_temp_item == p_node )
681 vlc_mutex_unlock( &p_playlist->object_lock );
682 vlc_object_release( p_playlist );
686 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
688 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
690 if( p_temp_item->pp_parents[i]->p_parent == p_node )
692 vlc_mutex_unlock( &p_playlist->object_lock );
693 vlc_object_release( p_playlist );
698 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
704 vlc_mutex_unlock( &p_playlist->object_lock );
707 vlc_object_release( p_playlist );
711 /* This method is usefull for instance to remove the selected children of an
712 already selected node */
713 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
716 for( i = 0 ; i < [o_items count] ; i++ )
718 for ( j = 0 ; j < [o_nodes count] ; j++ )
720 if( o_items == o_nodes)
722 if( j == i ) continue;
724 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
725 inNode: [[o_nodes objectAtIndex:j] pointerValue]
726 checkItemExistence: NO] )
728 [o_items removeObjectAtIndex:i];
729 /* We need to execute the next iteration with the same index
730 since the current item has been deleted */
739 - (IBAction)savePlaylist:(id)sender
741 intf_thread_t * p_intf = VLCIntf;
742 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
745 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
746 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
747 [o_save_panel setTitle: _NS("Save Playlist")];
748 [o_save_panel setPrompt: _NS("Save")];
750 if( [o_save_panel runModalForDirectory: nil
751 file: o_name] == NSOKButton )
753 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
758 /* When called retrieves the selected outlineview row and plays that node or item */
759 - (IBAction)playItem:(id)sender
761 intf_thread_t * p_intf = VLCIntf;
762 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
765 if( p_playlist != NULL )
767 playlist_item_t *p_item;
768 playlist_item_t *p_node = NULL;
771 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
775 if( p_item->i_children == -1 )
777 p_node = [self parentOfItem: p_item];
779 /* for( i = 0 ; i < p_item->i_parents ; i++ )
781 if( p_item->pp_parents[i]->i_view == i_current_view )
783 p_node = p_item->pp_parents[i]->p_parent;
790 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
792 p_item = p_node->pp_children[0];
799 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
801 vlc_object_release( p_playlist );
805 - (IBAction)servicesChange:(id)sender
807 NSMenuItem *o_mi = (NSMenuItem *)sender;
808 NSString *o_string = [o_mi representedObject];
809 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
811 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
812 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
814 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
816 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
817 [o_string cString] ) ? YES : NO];
819 i_current_view = VIEW_CATEGORY;
820 playlist_ViewUpdate( p_playlist, i_current_view );
821 vlc_object_release( p_playlist );
822 [self playlistUpdated];
826 - (IBAction)selectAll:(id)sender
828 [o_outline_view selectAll: nil];
831 - (IBAction)deleteItem:(id)sender
833 int i, i_count, i_row;
834 NSMutableArray *o_to_delete;
837 playlist_t * p_playlist;
838 intf_thread_t * p_intf = VLCIntf;
840 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
843 if ( p_playlist == NULL )
847 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
848 i_count = [o_to_delete count];
850 for( i = 0; i < i_count; i++ )
852 o_number = [o_to_delete lastObject];
853 i_row = [o_number intValue];
854 id o_item = [o_outline_view itemAtRow: i_row];
855 playlist_item_t *p_item = [o_item pointerValue];
856 [o_to_delete removeObject: o_number];
857 [o_outline_view deselectRow: i_row];
859 if( [[o_outline_view dataSource] outlineView:o_outline_view
860 numberOfChildrenOfItem: o_item] > 0 )
861 //is a node and not an item
863 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
864 [self isItem: p_playlist->status.p_item inNode:
865 ((playlist_item_t *)[o_item pointerValue])
866 checkItemExistence: NO] == YES )
868 // if current item is in selected node and is playing then stop playlist
869 playlist_Stop( p_playlist );
871 vlc_mutex_lock( &p_playlist->object_lock );
872 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
873 vlc_mutex_unlock( &p_playlist->object_lock );
877 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
878 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
880 playlist_Stop( p_playlist );
882 vlc_mutex_lock( &p_playlist->object_lock );
883 playlist_Delete( p_playlist, p_item->input.i_id );
884 vlc_mutex_unlock( &p_playlist->object_lock );
887 [self playlistUpdated];
888 vlc_object_release( p_playlist );
891 - (IBAction)sortNodeByName:(id)sender
893 [self sortNode: SORT_TITLE];
896 - (IBAction)sortNodeByAuthor:(id)sender
898 [self sortNode: SORT_AUTHOR];
901 - (void)sortNode:(int)i_mode
903 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
905 playlist_item_t * p_item;
907 if (p_playlist == NULL)
912 if( [o_outline_view selectedRow] > -1 )
914 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
918 /*If no item is selected, sort the whole playlist*/
920 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
921 p_item = p_view->p_root;
924 if( p_item->i_children > -1 ) // the item is a node
926 vlc_mutex_lock( &p_playlist->object_lock );
927 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
928 vlc_mutex_unlock( &p_playlist->object_lock );
934 for( i = 0 ; i < p_item->i_parents ; i++ )
936 if( p_item->pp_parents[i]->i_view == i_current_view )
938 vlc_mutex_lock( &p_playlist->object_lock );
939 playlist_RecursiveNodeSort( p_playlist,
940 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
941 vlc_mutex_unlock( &p_playlist->object_lock );
946 vlc_object_release( p_playlist );
947 [self playlistUpdated];
950 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
952 intf_thread_t * p_intf = VLCIntf;
953 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
956 if( p_playlist == NULL )
960 playlist_item_t *p_item;
962 BOOL b_rem = FALSE, b_dir = FALSE;
963 NSString *o_uri, *o_name;
968 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
969 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
970 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
972 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
973 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
975 int i_count, i_index;
976 struct statfs *mounts = NULL;
978 i_count = getmntinfo (&mounts, MNT_NOWAIT);
979 /* getmntinfo returns a pointer to static data. Do not free. */
980 for( i_index = 0 ; i_index < i_count; i_index++ )
982 NSMutableString *o_temp, *o_temp2;
983 o_temp = [NSMutableString stringWithString: o_uri];
984 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
985 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
986 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
987 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp2 length]) ];
989 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
991 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
995 /* If no name, then make a guess */
996 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
998 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
999 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1000 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1002 /* All of this is to make sure CD's play when you D&D them on VLC */
1003 /* Converts mountpoint to a /dev file */
1006 NSMutableString *o_temp;
1008 buf = (struct statfs *) malloc (sizeof(struct statfs));
1009 statfs( [o_uri fileSystemRepresentation], buf );
1010 psz_dev = strdup(buf->f_mntfromname);
1011 o_temp = [NSMutableString stringWithCString: psz_dev ];
1012 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1013 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1014 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:NULL range:NSMakeRange(0, [o_temp length]) ];
1018 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1024 for( i = 0; i < (int)[o_options count]; i++ )
1026 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1030 /* Recent documents menu */
1031 o_true_file = [NSURL fileURLWithPath: o_uri];
1032 if( o_true_file != nil )
1034 [[NSDocumentController sharedDocumentController]
1035 noteNewRecentDocumentURL: o_true_file];
1038 vlc_object_release( p_playlist );
1042 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1045 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1047 if( p_playlist == NULL )
1052 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1054 playlist_item_t *p_item;
1055 NSDictionary *o_one_item;
1058 o_one_item = [o_array objectAtIndex: i_item];
1059 p_item = [self createItem: o_one_item];
1066 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1068 if( i_item == 0 && !b_enqueue )
1070 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1073 vlc_object_release( p_playlist );
1076 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1079 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1081 if( p_playlist == NULL )
1086 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1088 playlist_item_t *p_item;
1089 NSDictionary *o_one_item;
1092 o_one_item = [o_array objectAtIndex: i_item];
1093 p_item = [self createItem: o_one_item];
1100 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1102 if( i_item == 0 && !b_enqueue )
1104 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1107 vlc_object_release( p_playlist );
1111 - (IBAction)handlePopUp:(id)sender
1114 intf_thread_t * p_intf = VLCIntf;
1115 vlc_value_t val1,val2;
1116 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1118 if( p_playlist == NULL )
1123 switch( [o_loop_popup indexOfSelectedItem] )
1128 var_Set( p_playlist, "loop", val1 );
1130 var_Set( p_playlist, "repeat", val1 );
1131 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1136 var_Set( p_playlist, "repeat", val1 );
1138 var_Set( p_playlist, "loop", val1 );
1139 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1143 var_Get( p_playlist, "repeat", &val1 );
1144 var_Get( p_playlist, "loop", &val2 );
1145 if( val1.b_bool || val2.b_bool )
1148 var_Set( p_playlist, "repeat", val1 );
1149 var_Set( p_playlist, "loop", val1 );
1150 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1154 vlc_object_release( p_playlist );
1155 [self playlistUpdated];
1158 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1160 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1162 playlist_item_t *p_selected_item;
1163 int i_current, i_selected_row;
1168 i_selected_row = [o_outline_view selectedRow];
1169 if (i_selected_row < 0)
1172 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1173 i_selected_row] pointerValue];
1175 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1178 NSString *o_current_name, *o_current_author;
1180 vlc_mutex_lock( &p_playlist->object_lock );
1181 o_current_name = [NSString stringWithUTF8String:
1182 p_item->pp_children[i_current]->input.psz_name];
1183 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1184 _("Meta-information"),_("Artist") );
1185 o_current_author = [NSString stringWithUTF8String: psz_temp];
1187 vlc_mutex_unlock( &p_playlist->object_lock );
1189 if( p_selected_item == p_item->pp_children[i_current] &&
1190 b_selected_item_met == NO )
1192 b_selected_item_met = YES;
1194 else if( p_selected_item == p_item->pp_children[i_current] &&
1195 b_selected_item_met == YES )
1197 vlc_object_release( p_playlist );
1200 else if( b_selected_item_met == YES &&
1201 ( [o_current_name rangeOfString:[o_search_field
1202 stringValue] options:NSCaseInsensitiveSearch ].length ||
1203 [o_current_author rangeOfString:[o_search_field
1204 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1206 vlc_object_release( p_playlist );
1207 /*Adds the parent items in the result array as well, so that we can
1209 return [NSMutableArray arrayWithObject: [NSValue
1210 valueWithPointer: p_item->pp_children[i_current]]];
1212 if( p_item->pp_children[i_current]->i_children > 0 )
1214 id o_result = [self subSearchItem:
1215 p_item->pp_children[i_current]];
1216 if( o_result != NULL )
1218 vlc_object_release( p_playlist );
1219 [o_result insertObject: [NSValue valueWithPointer:
1220 p_item->pp_children[i_current]] atIndex:0];
1225 vlc_object_release( p_playlist );
1229 - (IBAction)searchItem:(id)sender
1231 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1233 playlist_view_t * p_view;
1239 b_selected_item_met = NO;
1241 if( p_playlist == NULL )
1243 p_view = playlist_ViewFind( p_playlist, i_current_view );
1247 /*First, only search after the selected item:*
1248 *(b_selected_item_met = NO) */
1249 o_result = [self subSearchItem:p_view->p_root];
1250 if( o_result == NULL )
1252 /* If the first search failed, search again from the beginning */
1253 o_result = [self subSearchItem:p_view->p_root];
1255 if( o_result != NULL )
1258 if( [[o_result objectAtIndex: 0] pointerValue] ==
1259 p_playlist->p_general )
1264 for( i = i_start ; i < [o_result 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: [o_result count] - 1 ]
1277 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1278 [o_outline_view scrollRowToVisible: i_row];
1281 vlc_object_release( p_playlist );
1284 - (IBAction)recursiveExpandNode:(id)sender
1287 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1288 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1290 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1291 isItemExpandable: o_item] )
1293 for( i = 0 ; i < p_item->i_parents ; i++ )
1295 if( p_item->pp_parents[i]->i_view == i_current_view )
1297 o_item = [o_outline_dict objectForKey: [NSString
1298 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1304 /* We need to collapse the node first, since OSX refuses to recursively
1305 expand an already expanded node, even if children nodes are collapsed. */
1306 [o_outline_view collapseItem: o_item collapseChildren: YES];
1307 [o_outline_view expandItem: o_item expandChildren: YES];
1310 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1314 vlc_bool_t b_item_sel;
1316 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1318 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1319 [o_outline_view selectedRow] != -1 );
1320 b_rows = [o_outline_view numberOfRows] != 0;
1322 [o_mi_play setEnabled: b_item_sel];
1323 [o_mi_delete setEnabled: b_item_sel];
1324 [o_mi_selectall setEnabled: b_rows];
1325 [o_mi_info setEnabled: b_item_sel];
1326 [o_mi_recursive_expand setEnabled: b_item_sel];
1327 [o_mi_sort_name setEnabled: b_item_sel];
1328 [o_mi_sort_author setEnabled: b_item_sel];
1330 return( o_ctx_menu );
1333 - (void)outlineView: (NSTableView*)o_tv
1334 didClickTableColumn:(NSTableColumn *)o_tc
1336 int i_mode = 0, i_type;
1337 intf_thread_t *p_intf = VLCIntf;
1338 playlist_view_t *p_view;
1340 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1342 if( p_playlist == NULL )
1347 /* Check whether the selected table column header corresponds to a
1348 sortable table column*/
1349 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1351 vlc_object_release( p_playlist );
1355 p_view = playlist_ViewFind( p_playlist, i_current_view );
1357 if( o_tc_sortColumn == o_tc )
1359 b_isSortDescending = !b_isSortDescending;
1363 b_isSortDescending = VLC_FALSE;
1366 if( o_tc == o_tc_name )
1368 i_mode = SORT_TITLE;
1370 else if( o_tc == o_tc_author )
1372 i_mode = SORT_AUTHOR;
1375 if( b_isSortDescending )
1377 i_type = ORDER_REVERSE;
1381 i_type = ORDER_NORMAL;
1384 vlc_mutex_lock( &p_playlist->object_lock );
1385 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1386 vlc_mutex_unlock( &p_playlist->object_lock );
1388 vlc_object_release( p_playlist );
1389 [self playlistUpdated];
1391 o_tc_sortColumn = o_tc;
1392 [o_outline_view setHighlightedTableColumn:o_tc];
1394 if( b_isSortDescending )
1396 [o_outline_view setIndicatorImage:o_descendingSortingImage
1397 inTableColumn:o_tc];
1401 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1402 inTableColumn:o_tc];
1407 - (void)outlineView:(NSOutlineView *)outlineView
1408 willDisplayCell:(id)cell
1409 forTableColumn:(NSTableColumn *)tableColumn
1412 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1417 if( !p_playlist ) return;
1419 o_playing_item = [o_outline_dict objectForKey:
1420 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1422 if( [self isItem: [o_playing_item pointerValue] inNode:
1423 [item pointerValue] checkItemExistence: YES]
1424 || [o_playing_item isEqual: item] )
1426 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1430 [cell setFont: [NSFont systemFontOfSize: 0]];
1432 vlc_object_release( p_playlist );
1437 @implementation VLCPlaylist (NSOutlineViewDataSource)
1439 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1441 id o_value = [super outlineView: outlineView child: index ofItem: item];
1442 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1445 if( !p_playlist ) return nil;
1447 if( p_playlist->i_size >= 2 )
1449 [o_status_field setStringValue: [NSString stringWithFormat:
1450 _NS("%i items in playlist"), p_playlist->i_size]];
1454 if( p_playlist->i_size == 0 )
1456 [o_status_field setStringValue: [NSString stringWithFormat:
1457 _NS("no items in playlist"), p_playlist->i_size]];
1461 [o_status_field setStringValue: [NSString stringWithFormat:
1462 _NS("1 item in playlist"), p_playlist->i_size]];
1465 vlc_object_release( p_playlist );
1467 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1468 [o_value pointerValue]]];
1474 /* Required for drag & drop and reordering */
1475 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1478 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1481 /* First remove the items that were moved during the last drag & drop
1483 [o_items_array removeAllObjects];
1484 [o_nodes_array removeAllObjects];
1486 if( !p_playlist ) return NO;
1488 for( i = 0 ; i < [items count] ; i++ )
1490 id o_item = [items objectAtIndex: i];
1492 /* Refuse to move items that are not in the General Node
1493 (Service Discovery) */
1494 if( ![self isItem: [o_item pointerValue] inNode:
1495 p_playlist->p_general checkItemExistence: NO])
1497 vlc_object_release(p_playlist);
1500 /* Fill the items and nodes to move in 2 different arrays */
1501 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1502 [o_nodes_array addObject: o_item];
1504 [o_items_array addObject: o_item];
1507 /* Now we need to check if there are selected items that are in already
1508 selected nodes. In that case, we only want to move the nodes */
1509 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1510 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1513 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1515 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1517 if( j == i ) continue;
1518 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1519 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1521 [o_nodes_array removeObjectAtIndex:i];
1522 /* We need to execute the next iteration with the same index
1523 since the current item has been deleted */
1530 for( i = 0 ; i < [o_items_array count] ; i++ )
1532 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1534 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1535 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1537 [o_items_array removeObjectAtIndex:i];
1544 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1545 a Drop operation comçing from the playlist.
1546 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1549 [pboard declareTypes: [NSArray arrayWithObjects:
1550 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1551 [pboard setPropertyList:[NSArray array]
1552 forType:NSFilenamesPboardType];
1554 vlc_object_release(p_playlist);
1558 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1560 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1562 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1564 if( !p_playlist ) return NSDragOperationNone;
1566 /* We refuse to drop an item in anything else than a child of the General
1567 Node. We still accept items that would be root nodes of the outlineview
1568 however, to allow drop in an empty playlist.*/
1569 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1570 checkItemExistence: NO] || item == nil) )
1572 vlc_object_release(p_playlist);
1573 return NSDragOperationNone;
1576 /* Drop from the Playlist */
1577 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1580 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1582 /* We refuse to Drop in a child of an item we are moving */
1583 if( [self isItem: [item pointerValue] inNode:
1584 [[o_nodes_array objectAtIndex: i] pointerValue]
1585 checkItemExistence: NO] )
1587 vlc_object_release(p_playlist);
1588 return NSDragOperationNone;
1591 vlc_object_release(p_playlist);
1592 return NSDragOperationMove;
1595 /* Drop from the Finder */
1596 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1598 vlc_object_release(p_playlist);
1599 return NSDragOperationGeneric;
1601 vlc_object_release(p_playlist);
1602 return NSDragOperationNone;
1605 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1607 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1609 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1611 if( !p_playlist ) return NO;
1613 /* Drag & Drop inside the playlist */
1614 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1618 playlist_item_t *p_new_parent, *p_item = NULL;
1619 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1621 /* If the item is to be dropped as root item of the outline, make it a
1622 child of the General node.
1623 Else, choose the proposed parent as parent. */
1625 p_new_parent = p_playlist->p_general;
1627 p_new_parent = [item pointerValue];
1629 /* If the proposed parent is not a node, then use the parent node of
1631 if( p_new_parent->i_children <= 0 )
1634 playlist_item_t *p_temp_item = p_new_parent;
1635 p_new_parent = [self parentOfItem: p_new_parent];
1638 vlc_object_release(p_playlist);
1641 /* Calculate the position of the dropped item in this new parent:
1642 following the first proposed parent. */
1643 for( j = 0; j < p_new_parent->i_children; j++ )
1645 if( p_new_parent->pp_children[j] == p_temp_item )
1650 else if( j == p_new_parent->i_children - 1 )
1655 for( i = 0; i < [o_all_items count]; i++ )
1657 playlist_item_t *p_old_parent = NULL;
1658 int i_old_index = 0;
1660 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1661 p_old_parent = [self parentOfItem: p_item];
1664 /* We may need the old index later */
1665 if( p_new_parent == p_old_parent )
1668 for( j = 0; j < p_old_parent->i_children; j++ )
1670 if( p_old_parent->pp_children[j] == p_item )
1679 /* If we move the playing item in a different node or we move the
1680 node containing the playing item in a different node, then stop
1681 playback, or the playlist refuses to detach the item. */
1682 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1683 (( p_item == p_playlist->status.p_item &&
1684 p_new_parent != p_old_parent) ||
1685 ( p_item->i_children > 0 &&
1686 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1688 playlist_Stop( p_playlist );
1690 vlc_mutex_lock( &p_playlist->object_lock );
1691 // Acually detach the item from the old position
1692 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1694 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1698 /* Calculate the new index */
1701 /* If we move the item in the same node, we need to take into
1702 account that one item will be deleted */
1703 else if((p_new_parent == p_old_parent &&
1704 i_old_index < index + (int)i)
1705 || p_new_parent == p_playlist->p_general || index == 0 )
1706 i_new_index = index + i;
1708 i_new_index = index + i + 1;
1709 // Reattach the item to the new position
1710 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1711 p_new_parent, i_new_index );
1713 vlc_mutex_unlock( &p_playlist->object_lock );
1715 [self playlistUpdated];
1716 i_row = [o_outline_view rowForItem:[o_outline_dict
1717 objectForKey:[NSString stringWithFormat: @"%p",
1718 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1722 i_row = [o_outline_view rowForItem:[o_outline_dict
1723 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1726 [o_outline_view deselectAll: self];
1727 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1728 [o_outline_view scrollRowToVisible: i_row];
1730 vlc_object_release(p_playlist);
1734 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1737 playlist_item_t *p_node = [item pointerValue];
1739 NSArray *o_array = [NSArray array];
1740 NSArray *o_values = [[o_pasteboard propertyListForType:
1741 NSFilenamesPboardType]
1742 sortedArrayUsingSelector:
1743 @selector(caseInsensitiveCompare:)];
1745 for( i = 0; i < (int)[o_values count]; i++)
1747 NSDictionary *o_dic;
1748 o_dic = [NSDictionary dictionaryWithObject:[o_values
1749 objectAtIndex:i] forKey:@"ITEM_URL"];
1750 o_array = [o_array arrayByAddingObject: o_dic];
1755 [self appendArray: o_array atPos: index enqueue: YES];
1757 else if( p_node->i_children == -1 )
1760 playlist_item_t *p_real_node = NULL;
1762 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1764 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1766 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1769 if( i_counter == p_node->i_parents )
1771 vlc_object_release(p_playlist);
1775 [self appendNodeArray: o_array inNode: p_real_node
1776 atPos: index inView: i_current_view enqueue: YES];
1780 [self appendNodeArray: o_array inNode: p_node
1781 atPos: index inView: i_current_view enqueue: YES];
1783 vlc_object_release( p_playlist );
1786 vlc_object_release( p_playlist );
1790 /* Delegate method of NSWindow */
1791 /*- (void)windowWillClose:(NSNotification *)aNotification
1793 [o_btn_playlist setState: NSOffState];