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>
55 /*****************************************************************************
56 * VLCPlaylistView implementation
57 *****************************************************************************/
58 @implementation VLCPlaylistView
60 - (NSMenu *)menuForEvent:(NSEvent *)o_event
62 return( [[self delegate] menuForEvent: o_event] );
65 - (void)keyDown:(NSEvent *)o_event
69 if( [[o_event characters] length] )
71 key = [[o_event characters] characterAtIndex: 0];
76 case NSDeleteCharacter:
77 case NSDeleteFunctionKey:
78 case NSDeleteCharFunctionKey:
79 case NSBackspaceCharacter:
80 [[self delegate] deleteItem:self];
83 case NSEnterCharacter:
84 case NSCarriageReturnCharacter:
85 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
90 [super keyDown: o_event];
98 /*****************************************************************************
99 * VLCPlaylistCommon implementation
101 * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
102 * It contains the common methods and elements of these 2 entities.
103 *****************************************************************************/
104 @implementation VLCPlaylistCommon
108 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
110 i_current_view = VIEW_CATEGORY;
111 playlist_ViewUpdate( p_playlist, i_current_view );
113 [o_outline_view setTarget: self];
114 [o_outline_view setDelegate: self];
115 [o_outline_view setDataSource: self];
117 vlc_object_release( p_playlist );
123 [[o_tc_name headerCell] setStringValue:_NS("Name")];
124 [[o_tc_author headerCell] setStringValue:_NS("Author")];
125 [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
128 - (NSOutlineView *)outlineView
130 return o_outline_view;
133 - (playlist_item_t *)selectedPlaylistItem
135 return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
141 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
143 /* return the number of children for Obj-C pointer item */ /* DONE */
144 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
147 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
149 if( p_playlist == NULL )
151 if( outlineView != o_outline_view )
153 vlc_object_release( p_playlist );
160 playlist_view_t *p_view;
161 p_view = playlist_ViewFind( p_playlist, i_current_view );
162 if( p_view && p_view->p_root )
164 i_return = p_view->p_root->i_children;
165 if( i_current_view == VIEW_CATEGORY )
167 i_return--; /* remove the GENERAL item from the list */
168 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
174 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
176 i_return = p_item->i_children;
178 vlc_object_release( p_playlist );
186 /* return the child at index for the Obj-C pointer item */ /* DONE */
187 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
189 playlist_item_t *p_return = NULL;
190 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
194 if( p_playlist == NULL )
200 playlist_view_t *p_view;
201 p_view = playlist_ViewFind( p_playlist, i_current_view );
202 if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
204 if( i_current_view == VIEW_CATEGORY )
206 if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
208 p_return = p_playlist->p_general->pp_children[index];
210 else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
212 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
218 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
219 if( p_item && index < p_item->i_children && index >= 0 )
220 p_return = p_item->pp_children[index];
224 vlc_object_release( p_playlist );
226 o_value = [[NSValue valueWithPointer: p_return] retain];
231 /* is the item expandable */
232 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
235 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
237 if( p_playlist == NULL )
243 playlist_view_t *p_view;
244 p_view = playlist_ViewFind( p_playlist, i_current_view );
245 if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
247 if( i_current_view == VIEW_CATEGORY )
250 i_return += p_playlist->p_general->i_children;
255 playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
257 i_return = p_item->i_children;
259 vlc_object_release( p_playlist );
267 /* retrieve the string values for the cells */
268 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
271 intf_thread_t *p_intf = VLCIntf;
272 playlist_t *p_playlist;
273 playlist_item_t *p_item;
275 if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
277 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
279 if( p_playlist == NULL )
284 p_item = (playlist_item_t *)[item pointerValue];
288 vlc_object_release( p_playlist );
292 if( [[o_tc identifier] isEqualToString:@"1"] )
294 o_value = [NSString stringWithUTF8String:
295 p_item->input.psz_name];
296 if( o_value == NULL )
297 o_value = [NSString stringWithCString:
298 p_item->input.psz_name];
300 else if( [[o_tc identifier] isEqualToString:@"2"] )
303 psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
305 if( psz_temp == NULL )
309 o_value = [NSString stringWithUTF8String: psz_temp];
310 if( o_value == NULL )
312 o_value = [NSString stringWithCString: psz_temp];
317 else if( [[o_tc identifier] isEqualToString:@"3"] )
319 char psz_duration[MSTRTIME_MAX_SIZE];
320 mtime_t dur = p_item->input.i_duration;
323 secstotimestr( psz_duration, dur/1000000 );
324 o_value = [NSString stringWithUTF8String: psz_duration];
328 o_value = @"-:--:--";
331 vlc_object_release( p_playlist );
338 /*****************************************************************************
339 * VLCPlaylistWizard implementation
340 *****************************************************************************/
341 @implementation VLCPlaylistWizard
343 - (IBAction)reloadOutlineView
345 /* Only reload the outlineview if the wizard window is open since this can
346 be quite long on big playlists */
347 if( [[o_outline_view window] isVisible] )
349 [o_outline_view reloadData];
355 /*****************************************************************************
356 * VLCPlaylist implementation
357 *****************************************************************************/
358 @implementation VLCPlaylist
365 o_outline_dict = [[NSMutableDictionary alloc] init];
366 o_nodes_array = [[NSMutableArray alloc] init];
367 o_items_array = [[NSMutableArray alloc] init];
377 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
379 vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
384 [super awakeFromNib];
386 [o_outline_view setDoubleAction: @selector(playItem:)];
388 [o_outline_view registerForDraggedTypes:
389 [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
390 [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
392 /* We need to check whether _defaultTableHeaderSortImage exists, since it
393 belongs to an Apple hidden private API, and then can "disapear" at any time*/
395 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
397 o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
401 o_ascendingSortingImage = nil;
404 if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
406 o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
410 o_descendingSortingImage = nil;
413 o_tc_sortColumn = nil;
415 for( i_index = 0; i_index < p_list->i_count; i_index++ )
418 module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
420 if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
422 /* create the menu entries used in the playlist menu */
423 o_lmi = [[o_mi_services submenu] addItemWithTitle:
424 [NSString stringWithUTF8String:
425 p_parser->psz_longname ? p_parser->psz_longname :
426 ( p_parser->psz_shortname ? p_parser->psz_shortname:
427 p_parser->psz_object_name)]
428 action: @selector(servicesChange:)
430 [o_lmi setTarget: self];
431 [o_lmi setRepresentedObject:
432 [NSString stringWithCString: p_parser->psz_object_name]];
433 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
434 p_parser->psz_object_name ) )
435 [o_lmi setState: NSOnState];
437 /* create the menu entries for the main menu */
438 o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
439 [NSString stringWithUTF8String:
440 p_parser->psz_longname ? p_parser->psz_longname :
441 ( p_parser->psz_shortname ? p_parser->psz_shortname:
442 p_parser->psz_object_name)]
443 action: @selector(servicesChange:)
445 [o_lmi setTarget: self];
446 [o_lmi setRepresentedObject:
447 [NSString stringWithCString: p_parser->psz_object_name]];
448 if( playlist_IsServicesDiscoveryLoaded( p_playlist,
449 p_parser->psz_object_name ) )
450 [o_lmi setState: NSOnState];
453 vlc_list_release( p_list );
454 vlc_object_release( p_playlist );
456 /* Change the simple textfield into a searchField if we can... */
458 if( MACOS_VERSION >= 10.3 )
460 NSView *o_parentview = [o_status_field superview];
461 NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
462 [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
463 [o_better_search_field setDelegate:self];
464 [[NSNotificationCenter defaultCenter] addObserver: self
465 selector: @selector(searchfieldChanged:)
466 name: NSControlTextDidChangeNotification
467 object: o_better_search_field];
469 [o_better_search_field setTarget:self];
470 [o_better_search_field setAction:@selector(searchItem:)];
472 [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
473 [o_parentview addSubview:o_better_search_field];
474 [o_search_field setHidden:YES];
477 //[self playlistUpdated];
480 - (void)searchfieldChanged:(NSNotification *)o_notification
482 [o_search_field setStringValue:[[o_notification object] stringValue]];
489 [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
490 [o_mi_play setTitle: _NS("Play")];
491 [o_mi_delete setTitle: _NS("Delete")];
492 [o_mi_recursive_expand setTitle: _NS("Expand Node")];
493 [o_mi_selectall setTitle: _NS("Select All")];
494 [o_mi_info setTitle: _NS("Properties")];
495 [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
496 [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
497 [o_mi_services setTitle: _NS("Services discovery")];
498 [o_status_field setStringValue: [NSString stringWithFormat:
499 _NS("no items in playlist")]];
501 [o_random_ckb setTitle: _NS("Random")];
503 [o_search_button setTitle: _NS("Search")];
505 [o_search_field setToolTip: _NS("Search in Playlist")];
506 [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
507 [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
508 [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
511 - (void)playlistUpdated
515 /* Clear indications of any existing column sorting*/
516 for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
518 [o_outline_view setIndicatorImage:nil inTableColumn:
519 [[o_outline_view tableColumns] objectAtIndex:i]];
522 [o_outline_view setHighlightedTableColumn:nil];
523 o_tc_sortColumn = nil;
524 // TODO Find a way to keep the dict size to a minimum
525 //[o_outline_dict removeAllObjects];
526 [o_outline_view reloadData];
527 [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
528 [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
531 - (void)playModeUpdated
533 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
535 vlc_value_t val, val2;
537 if( p_playlist == NULL )
542 var_Get( p_playlist, "loop", &val2 );
543 var_Get( p_playlist, "repeat", &val );
544 if( val.b_bool == VLC_TRUE )
546 [o_loop_popup selectItemAtIndex: 1];
548 else if( val2.b_bool == VLC_TRUE )
550 [o_loop_popup selectItemAtIndex: 2];
554 [o_loop_popup selectItemAtIndex: 0];
557 var_Get( p_playlist, "random", &val );
558 [o_random_ckb setState: val.b_bool];
560 vlc_object_release( p_playlist );
563 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
566 for( i = 0 ; i < p_item->i_parents; i++ )
568 if( p_item->pp_parents[i]->i_view == i_current_view )
570 return p_item->pp_parents[i]->p_parent;
576 - (void)updateRowSelection
582 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
584 playlist_item_t *p_item, *p_temp_item;
585 NSMutableArray *o_array = [NSMutableArray array];
587 if( p_playlist == NULL )
590 p_item = p_playlist->status.p_item;
593 vlc_object_release(p_playlist);
597 p_temp_item = p_item;
598 while( p_temp_item->i_parents > 0 )
600 [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
602 p_temp_item = [self parentOfItem: p_temp_item];
603 /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
605 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
607 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
613 for (j = 0 ; j < [o_array count] - 1 ; j++)
616 if( ( o_item = [o_outline_dict objectForKey:
617 [NSString stringWithFormat: @"%p",
618 [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
619 [o_outline_view expandItem: o_item];
623 i_row = [o_outline_view rowForItem:[o_outline_dict
624 objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
626 [o_outline_view selectRow: i_row byExtendingSelection: NO];
627 [o_outline_view scrollRowToVisible: i_row];
629 vlc_object_release(p_playlist);
632 /* 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
633 deleted. We don't do it when not required since this verification takes
634 quite a long time on big playlists (yes, pretty hacky). */
635 - (BOOL)isItem: (playlist_item_t *)p_item
636 inNode: (playlist_item_t *)p_node
637 checkItemExistence:(BOOL)b_check
640 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
642 playlist_item_t *p_temp_item = p_item;
644 if( p_playlist == NULL )
649 if( p_node == p_item )
651 vlc_object_release(p_playlist);
655 if( p_node->i_children < 1)
657 vlc_object_release(p_playlist);
664 vlc_mutex_lock( &p_playlist->object_lock );
668 /* Since outlineView: willDisplayCell:... may call this function with
669 p_items that don't exist anymore, first check if the item is still
670 in the playlist. Any cleaner solution welcomed. */
671 for( i = 0; i < p_playlist->i_all_size; i++ )
673 if( p_playlist->pp_all_items[i] == p_item ) break;
674 else if ( i == p_playlist->i_all_size - 1 )
676 vlc_object_release( p_playlist );
677 vlc_mutex_unlock( &p_playlist->object_lock );
683 while( p_temp_item->i_parents > 0 )
685 p_temp_item = [self parentOfItem: p_temp_item];
686 if( p_temp_item == p_node )
688 vlc_mutex_unlock( &p_playlist->object_lock );
689 vlc_object_release( p_playlist );
693 /* for( i = 0; i < p_temp_item->i_parents ; i++ )
695 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
697 if( p_temp_item->pp_parents[i]->p_parent == p_node )
699 vlc_mutex_unlock( &p_playlist->object_lock );
700 vlc_object_release( p_playlist );
705 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
711 vlc_mutex_unlock( &p_playlist->object_lock );
714 vlc_object_release( p_playlist );
718 /* This method is usefull for instance to remove the selected children of an
719 already selected node */
720 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
723 for( i = 0 ; i < [o_items count] ; i++ )
725 for ( j = 0 ; j < [o_nodes count] ; j++ )
727 if( o_items == o_nodes)
729 if( j == i ) continue;
731 if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
732 inNode: [[o_nodes objectAtIndex:j] pointerValue]
733 checkItemExistence: NO] )
735 [o_items removeObjectAtIndex:i];
736 /* We need to execute the next iteration with the same index
737 since the current item has been deleted */
746 - (IBAction)savePlaylist:(id)sender
748 intf_thread_t * p_intf = VLCIntf;
749 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
752 NSSavePanel *o_save_panel = [NSSavePanel savePanel];
753 NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
754 [o_save_panel setTitle: _NS("Save Playlist")];
755 [o_save_panel setPrompt: _NS("Save")];
757 if( [o_save_panel runModalForDirectory: nil
758 file: o_name] == NSOKButton )
760 playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
762 vlc_object_release( p_playlist );
766 /* When called retrieves the selected outlineview row and plays that node or item */
767 - (IBAction)playItem:(id)sender
769 intf_thread_t * p_intf = VLCIntf;
770 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
773 if( p_playlist != NULL )
775 playlist_item_t *p_item;
776 playlist_item_t *p_node = NULL;
779 p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
783 if( p_item->i_children == -1 )
785 p_node = [self parentOfItem: p_item];
787 /* for( i = 0 ; i < p_item->i_parents ; i++ )
789 if( p_item->pp_parents[i]->i_view == i_current_view )
791 p_node = p_item->pp_parents[i]->p_parent;
798 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
800 p_item = p_node->pp_children[0];
807 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
809 vlc_object_release( p_playlist );
813 - (IBAction)servicesChange:(id)sender
815 NSMenuItem *o_mi = (NSMenuItem *)sender;
816 NSString *o_string = [o_mi representedObject];
817 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
819 if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
820 playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
822 playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
824 [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
825 [o_string cString] ) ? YES : NO];
827 i_current_view = VIEW_CATEGORY;
828 playlist_ViewUpdate( p_playlist, i_current_view );
829 vlc_object_release( p_playlist );
830 [self playlistUpdated];
834 - (IBAction)selectAll:(id)sender
836 [o_outline_view selectAll: nil];
839 - (IBAction)deleteItem:(id)sender
841 int i, i_count, i_row;
842 NSMutableArray *o_to_delete;
845 playlist_t * p_playlist;
846 intf_thread_t * p_intf = VLCIntf;
848 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
851 if ( p_playlist == NULL )
855 o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
856 i_count = [o_to_delete count];
858 for( i = 0; i < i_count; i++ )
860 o_number = [o_to_delete lastObject];
861 i_row = [o_number intValue];
862 id o_item = [o_outline_view itemAtRow: i_row];
863 playlist_item_t *p_item = [o_item pointerValue];
864 [o_to_delete removeObject: o_number];
865 [o_outline_view deselectRow: i_row];
867 if( [[o_outline_view dataSource] outlineView:o_outline_view
868 numberOfChildrenOfItem: o_item] > 0 )
869 //is a node and not an item
871 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
872 [self isItem: p_playlist->status.p_item inNode:
873 ((playlist_item_t *)[o_item pointerValue])
874 checkItemExistence: NO] == YES )
876 // if current item is in selected node and is playing then stop playlist
877 playlist_Stop( p_playlist );
879 vlc_mutex_lock( &p_playlist->object_lock );
880 playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
881 vlc_mutex_unlock( &p_playlist->object_lock );
885 if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
886 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
888 playlist_Stop( p_playlist );
890 vlc_mutex_lock( &p_playlist->object_lock );
891 playlist_Delete( p_playlist, p_item->input.i_id );
892 vlc_mutex_unlock( &p_playlist->object_lock );
895 [self playlistUpdated];
896 vlc_object_release( p_playlist );
899 - (IBAction)sortNodeByName:(id)sender
901 [self sortNode: SORT_TITLE];
904 - (IBAction)sortNodeByAuthor:(id)sender
906 [self sortNode: SORT_AUTHOR];
909 - (void)sortNode:(int)i_mode
911 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
913 playlist_item_t * p_item;
915 if (p_playlist == NULL)
920 if( [o_outline_view selectedRow] > -1 )
922 p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
926 /*If no item is selected, sort the whole playlist*/
928 playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
929 p_item = p_view->p_root;
932 if( p_item->i_children > -1 ) // the item is a node
934 vlc_mutex_lock( &p_playlist->object_lock );
935 playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
936 vlc_mutex_unlock( &p_playlist->object_lock );
942 for( i = 0 ; i < p_item->i_parents ; i++ )
944 if( p_item->pp_parents[i]->i_view == i_current_view )
946 vlc_mutex_lock( &p_playlist->object_lock );
947 playlist_RecursiveNodeSort( p_playlist,
948 p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
949 vlc_mutex_unlock( &p_playlist->object_lock );
954 vlc_object_release( p_playlist );
955 [self playlistUpdated];
958 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
960 intf_thread_t * p_intf = VLCIntf;
961 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
964 if( p_playlist == NULL )
968 playlist_item_t *p_item;
970 BOOL b_rem = FALSE, b_dir = FALSE;
971 NSString *o_uri, *o_name;
976 o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
977 o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
978 o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
980 /* Find the name for a disc entry ( i know, can you believe the trouble?) */
981 if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
983 int i_count, i_index;
984 struct statfs *mounts = NULL;
986 i_count = getmntinfo (&mounts, MNT_NOWAIT);
987 /* getmntinfo returns a pointer to static data. Do not free. */
988 for( i_index = 0 ; i_index < i_count; i_index++ )
990 NSMutableString *o_temp, *o_temp2;
991 o_temp = [NSMutableString stringWithString: o_uri];
992 o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
993 [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
994 [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
995 [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
997 if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
999 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
1003 /* If no name, then make a guess */
1004 if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
1006 if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
1007 [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
1008 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem )
1010 /* All of this is to make sure CD's play when you D&D them on VLC */
1011 /* Converts mountpoint to a /dev file */
1014 NSMutableString *o_temp;
1016 buf = (struct statfs *) malloc (sizeof(struct statfs));
1017 statfs( [o_uri fileSystemRepresentation], buf );
1018 psz_dev = strdup(buf->f_mntfromname);
1019 o_temp = [NSMutableString stringWithCString: psz_dev ];
1020 [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
1021 [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1022 [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
1026 p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
1032 for( i = 0; i < (int)[o_options count]; i++ )
1034 playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
1038 /* Recent documents menu */
1039 o_true_file = [NSURL fileURLWithPath: o_uri];
1040 if( o_true_file != nil )
1042 [[NSDocumentController sharedDocumentController]
1043 noteNewRecentDocumentURL: o_true_file];
1046 vlc_object_release( p_playlist );
1050 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
1053 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1055 if( p_playlist == NULL )
1060 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1062 playlist_item_t *p_item;
1063 NSDictionary *o_one_item;
1066 o_one_item = [o_array objectAtIndex: i_item];
1067 p_item = [self createItem: o_one_item];
1074 playlist_AddItem( p_playlist, p_item, PLAYLIST_APPEND, i_position == -1 ? PLAYLIST_END : i_position + i_item );
1076 if( i_item == 0 && !b_enqueue )
1078 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1081 vlc_object_release( p_playlist );
1084 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
1087 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1089 if( p_playlist == NULL )
1094 for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
1096 playlist_item_t *p_item;
1097 NSDictionary *o_one_item;
1100 o_one_item = [o_array objectAtIndex: i_item];
1101 p_item = [self createItem: o_one_item];
1108 playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_APPEND, i_position + i_item );
1110 if( i_item == 0 && !b_enqueue )
1112 playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
1115 vlc_object_release( p_playlist );
1119 - (IBAction)handlePopUp:(id)sender
1122 intf_thread_t * p_intf = VLCIntf;
1123 vlc_value_t val1,val2;
1124 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1126 if( p_playlist == NULL )
1131 switch( [o_loop_popup indexOfSelectedItem] )
1136 var_Set( p_playlist, "loop", val1 );
1138 var_Set( p_playlist, "repeat", val1 );
1139 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
1144 var_Set( p_playlist, "repeat", val1 );
1146 var_Set( p_playlist, "loop", val1 );
1147 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
1151 var_Get( p_playlist, "repeat", &val1 );
1152 var_Get( p_playlist, "loop", &val2 );
1153 if( val1.b_bool || val2.b_bool )
1156 var_Set( p_playlist, "repeat", val1 );
1157 var_Set( p_playlist, "loop", val1 );
1158 vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
1162 vlc_object_release( p_playlist );
1163 [self playlistUpdated];
1166 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
1168 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1170 playlist_item_t *p_selected_item;
1171 int i_current, i_selected_row;
1176 i_selected_row = [o_outline_view selectedRow];
1177 if (i_selected_row < 0)
1180 p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
1181 i_selected_row] pointerValue];
1183 for( i_current = 0; i_current < p_item->i_children ; i_current++ )
1186 NSString *o_current_name, *o_current_author;
1188 vlc_mutex_lock( &p_playlist->object_lock );
1189 o_current_name = [NSString stringWithUTF8String:
1190 p_item->pp_children[i_current]->input.psz_name];
1191 psz_temp = vlc_input_item_GetInfo( &p_item->input ,
1192 _("Meta-information"),_("Artist") );
1193 o_current_author = [NSString stringWithUTF8String: psz_temp];
1195 vlc_mutex_unlock( &p_playlist->object_lock );
1197 if( p_selected_item == p_item->pp_children[i_current] &&
1198 b_selected_item_met == NO )
1200 b_selected_item_met = YES;
1202 else if( p_selected_item == p_item->pp_children[i_current] &&
1203 b_selected_item_met == YES )
1205 vlc_object_release( p_playlist );
1208 else if( b_selected_item_met == YES &&
1209 ( [o_current_name rangeOfString:[o_search_field
1210 stringValue] options:NSCaseInsensitiveSearch ].length ||
1211 [o_current_author rangeOfString:[o_search_field
1212 stringValue] options:NSCaseInsensitiveSearch ].length ) )
1214 vlc_object_release( p_playlist );
1215 /*Adds the parent items in the result array as well, so that we can
1217 return [NSMutableArray arrayWithObject: [NSValue
1218 valueWithPointer: p_item->pp_children[i_current]]];
1220 if( p_item->pp_children[i_current]->i_children > 0 )
1222 id o_result = [self subSearchItem:
1223 p_item->pp_children[i_current]];
1224 if( o_result != NULL )
1226 vlc_object_release( p_playlist );
1227 [o_result insertObject: [NSValue valueWithPointer:
1228 p_item->pp_children[i_current]] atIndex:0];
1233 vlc_object_release( p_playlist );
1237 - (IBAction)searchItem:(id)sender
1239 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1241 playlist_view_t * p_view;
1247 b_selected_item_met = NO;
1249 if( p_playlist == NULL )
1251 p_view = playlist_ViewFind( p_playlist, i_current_view );
1255 /*First, only search after the selected item:*
1256 *(b_selected_item_met = NO) */
1257 o_result = [self subSearchItem:p_view->p_root];
1258 if( o_result == NULL )
1260 /* If the first search failed, search again from the beginning */
1261 o_result = [self subSearchItem:p_view->p_root];
1263 if( o_result != NULL )
1266 if( [[o_result objectAtIndex: 0] pointerValue] ==
1267 p_playlist->p_general )
1272 for( i = i_start ; i < [o_result count] - 1 ; i++ )
1274 [o_outline_view expandItem: [o_outline_dict objectForKey:
1275 [NSString stringWithFormat: @"%p",
1276 [[o_result objectAtIndex: i] pointerValue]]]];
1278 i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
1279 [NSString stringWithFormat: @"%p",
1280 [[o_result objectAtIndex: [o_result count] - 1 ]
1285 [o_outline_view selectRow:i_row byExtendingSelection: NO];
1286 [o_outline_view scrollRowToVisible: i_row];
1289 vlc_object_release( p_playlist );
1292 - (IBAction)recursiveExpandNode:(id)sender
1295 id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
1296 playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
1298 if( ![[o_outline_view dataSource] outlineView: o_outline_view
1299 isItemExpandable: o_item] )
1301 for( i = 0 ; i < p_item->i_parents ; i++ )
1303 if( p_item->pp_parents[i]->i_view == i_current_view )
1305 o_item = [o_outline_dict objectForKey: [NSString
1306 stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
1312 /* We need to collapse the node first, since OSX refuses to recursively
1313 expand an already expanded node, even if children nodes are collapsed. */
1314 [o_outline_view collapseItem: o_item collapseChildren: YES];
1315 [o_outline_view expandItem: o_item expandChildren: YES];
1318 - (NSMenu *)menuForEvent:(NSEvent *)o_event
1322 vlc_bool_t b_item_sel;
1324 pt = [o_outline_view convertPoint: [o_event locationInWindow]
1326 b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
1327 [o_outline_view selectedRow] != -1 );
1328 b_rows = [o_outline_view numberOfRows] != 0;
1330 [o_mi_play setEnabled: b_item_sel];
1331 [o_mi_delete setEnabled: b_item_sel];
1332 [o_mi_selectall setEnabled: b_rows];
1333 [o_mi_info setEnabled: b_item_sel];
1334 [o_mi_recursive_expand setEnabled: b_item_sel];
1335 [o_mi_sort_name setEnabled: b_item_sel];
1336 [o_mi_sort_author setEnabled: b_item_sel];
1338 return( o_ctx_menu );
1341 - (void)outlineView: (NSTableView*)o_tv
1342 didClickTableColumn:(NSTableColumn *)o_tc
1344 int i_mode = 0, i_type;
1345 intf_thread_t *p_intf = VLCIntf;
1346 playlist_view_t *p_view;
1348 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1350 if( p_playlist == NULL )
1355 /* Check whether the selected table column header corresponds to a
1356 sortable table column*/
1357 if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
1359 vlc_object_release( p_playlist );
1363 p_view = playlist_ViewFind( p_playlist, i_current_view );
1365 if( o_tc_sortColumn == o_tc )
1367 b_isSortDescending = !b_isSortDescending;
1371 b_isSortDescending = VLC_FALSE;
1374 if( o_tc == o_tc_name )
1376 i_mode = SORT_TITLE;
1378 else if( o_tc == o_tc_author )
1380 i_mode = SORT_AUTHOR;
1383 if( b_isSortDescending )
1385 i_type = ORDER_REVERSE;
1389 i_type = ORDER_NORMAL;
1392 vlc_mutex_lock( &p_playlist->object_lock );
1393 playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
1394 vlc_mutex_unlock( &p_playlist->object_lock );
1396 vlc_object_release( p_playlist );
1397 [self playlistUpdated];
1399 o_tc_sortColumn = o_tc;
1400 [o_outline_view setHighlightedTableColumn:o_tc];
1402 if( b_isSortDescending )
1404 [o_outline_view setIndicatorImage:o_descendingSortingImage
1405 inTableColumn:o_tc];
1409 [o_outline_view setIndicatorImage:o_ascendingSortingImage
1410 inTableColumn:o_tc];
1415 - (void)outlineView:(NSOutlineView *)outlineView
1416 willDisplayCell:(id)cell
1417 forTableColumn:(NSTableColumn *)tableColumn
1420 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1425 if( !p_playlist ) return;
1427 o_playing_item = [o_outline_dict objectForKey:
1428 [NSString stringWithFormat:@"%p", p_playlist->status.p_item]];
1430 if( [self isItem: [o_playing_item pointerValue] inNode:
1431 [item pointerValue] checkItemExistence: YES]
1432 || [o_playing_item isEqual: item] )
1434 [cell setFont: [NSFont boldSystemFontOfSize: 0]];
1438 [cell setFont: [NSFont systemFontOfSize: 0]];
1440 vlc_object_release( p_playlist );
1445 @implementation VLCPlaylist (NSOutlineViewDataSource)
1447 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
1449 id o_value = [super outlineView: outlineView child: index ofItem: item];
1450 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1453 if( !p_playlist ) return nil;
1455 if( p_playlist->i_size >= 2 )
1457 [o_status_field setStringValue: [NSString stringWithFormat:
1458 _NS("%i items in playlist"), p_playlist->i_size]];
1462 if( p_playlist->i_size == 0 )
1464 [o_status_field setStringValue: [NSString stringWithFormat:
1465 _NS("no items in playlist"), p_playlist->i_size]];
1469 [o_status_field setStringValue: [NSString stringWithFormat:
1470 _NS("1 item in playlist"), p_playlist->i_size]];
1473 vlc_object_release( p_playlist );
1475 [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
1476 [o_value pointerValue]]];
1482 /* Required for drag & drop and reordering */
1483 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
1486 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1489 /* First remove the items that were moved during the last drag & drop
1491 [o_items_array removeAllObjects];
1492 [o_nodes_array removeAllObjects];
1494 if( !p_playlist ) return NO;
1496 for( i = 0 ; i < [items count] ; i++ )
1498 id o_item = [items objectAtIndex: i];
1500 /* Refuse to move items that are not in the General Node
1501 (Service Discovery) */
1502 if( ![self isItem: [o_item pointerValue] inNode:
1503 p_playlist->p_general checkItemExistence: NO])
1505 vlc_object_release(p_playlist);
1508 /* Fill the items and nodes to move in 2 different arrays */
1509 if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
1510 [o_nodes_array addObject: o_item];
1512 [o_items_array addObject: o_item];
1515 /* Now we need to check if there are selected items that are in already
1516 selected nodes. In that case, we only want to move the nodes */
1517 [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
1518 [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
1523 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1525 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1527 if( j == i ) continue;
1528 if( [self isItem: [[o_nodes_array objectAtIndex:i] pointerValue]
1529 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1531 [o_nodes_array removeObjectAtIndex:i];
1532 /* We need to execute the next iteration with the same index
1533 since the current item has been deleted */
1540 for( i = 0 ; i < [o_items_array count] ; i++ )
1542 for ( j = 0 ; j < [o_nodes_array count] ; j++ )
1544 if( [self isItem: [[o_items_array objectAtIndex:i] pointerValue]
1545 inNode: [[o_nodes_array objectAtIndex:j] pointerValue]] )
1547 [o_items_array removeObjectAtIndex:i];
1554 /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
1555 a Drop operation comçing from the playlist.
1556 We need to add NSFilenamesPboardType otherwise the outlineview refuses
1559 [pboard declareTypes: [NSArray arrayWithObjects:
1560 @"VLCPlaylistItemPboardType",NSFilenamesPboardType, nil] owner: self];
1561 [pboard setPropertyList:[NSArray array]
1562 forType:NSFilenamesPboardType];
1564 vlc_object_release(p_playlist);
1568 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
1570 playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1572 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1574 if( !p_playlist ) return NSDragOperationNone;
1576 /* We refuse to drop an item in anything else than a child of the General
1577 Node. We still accept items that would be root nodes of the outlineview
1578 however, to allow drop in an empty playlist.*/
1579 if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
1580 checkItemExistence: NO] || item == nil) )
1582 vlc_object_release(p_playlist);
1583 return NSDragOperationNone;
1586 /* Drop from the Playlist */
1587 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1590 for( i = 0 ; i < [o_nodes_array count] ; i++ )
1592 /* We refuse to Drop in a child of an item we are moving */
1593 if( [self isItem: [item pointerValue] inNode:
1594 [[o_nodes_array objectAtIndex: i] pointerValue]
1595 checkItemExistence: NO] )
1597 vlc_object_release(p_playlist);
1598 return NSDragOperationNone;
1601 vlc_object_release(p_playlist);
1602 return NSDragOperationMove;
1605 /* Drop from the Finder */
1606 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1608 vlc_object_release(p_playlist);
1609 return NSDragOperationGeneric;
1611 vlc_object_release(p_playlist);
1612 return NSDragOperationNone;
1615 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
1617 playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
1619 NSPasteboard *o_pasteboard = [info draggingPasteboard];
1621 if( !p_playlist ) return NO;
1623 /* Drag & Drop inside the playlist */
1624 if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
1628 playlist_item_t *p_new_parent, *p_item = NULL;
1629 NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
1631 /* If the item is to be dropped as root item of the outline, make it a
1632 child of the General node.
1633 Else, choose the proposed parent as parent. */
1635 p_new_parent = p_playlist->p_general;
1637 p_new_parent = [item pointerValue];
1639 /* If the proposed parent is not a node, then use the parent node of
1641 if( p_new_parent->i_children <= 0 )
1644 playlist_item_t *p_temp_item = p_new_parent;
1645 p_new_parent = [self parentOfItem: p_new_parent];
1648 vlc_object_release(p_playlist);
1651 /* Calculate the position of the dropped item in this new parent:
1652 following the first proposed parent. */
1653 for( j = 0; j < p_new_parent->i_children; j++ )
1655 if( p_new_parent->pp_children[j] == p_temp_item )
1660 else if( j == p_new_parent->i_children - 1 )
1665 for( i = 0; i < [o_all_items count]; i++ )
1667 playlist_item_t *p_old_parent = NULL;
1668 int i_old_index = 0;
1670 p_item = [[o_all_items objectAtIndex:i] pointerValue];
1671 p_old_parent = [self parentOfItem: p_item];
1674 /* We may need the old index later */
1675 if( p_new_parent == p_old_parent )
1678 for( j = 0; j < p_old_parent->i_children; j++ )
1680 if( p_old_parent->pp_children[j] == p_item )
1689 /* If we move the playing item in a different node or we move the
1690 node containing the playing item in a different node, then stop
1691 playback, or the playlist refuses to detach the item. */
1692 /* if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
1693 (( p_item == p_playlist->status.p_item &&
1694 p_new_parent != p_old_parent) ||
1695 ( p_item->i_children > 0 &&
1696 [self isItem: p_playlist->status.p_item inNode:p_item] == YES))
1698 playlist_Stop( p_playlist );
1700 vlc_mutex_lock( &p_playlist->object_lock );
1701 // Acually detach the item from the old position
1702 if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
1704 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
1708 /* Calculate the new index */
1711 /* If we move the item in the same node, we need to take into
1712 account that one item will be deleted */
1713 else if((p_new_parent == p_old_parent &&
1714 i_old_index < index + (int)i)
1715 || p_new_parent == p_playlist->p_general || index == 0 )
1716 i_new_index = index + i;
1718 i_new_index = index + i + 1;
1719 // Reattach the item to the new position
1720 playlist_NodeInsert( p_playlist, i_current_view, p_item,
1721 p_new_parent, i_new_index );
1723 vlc_mutex_unlock( &p_playlist->object_lock );
1725 [self playlistUpdated];
1726 i_row = [o_outline_view rowForItem:[o_outline_dict
1727 objectForKey:[NSString stringWithFormat: @"%p",
1728 [[o_all_items objectAtIndex: 0] pointerValue]]]];
1732 i_row = [o_outline_view rowForItem:[o_outline_dict
1733 objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
1736 [o_outline_view deselectAll: self];
1737 [o_outline_view selectRow: i_row byExtendingSelection: NO];
1738 [o_outline_view scrollRowToVisible: i_row];
1740 vlc_object_release(p_playlist);
1744 else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
1747 playlist_item_t *p_node = [item pointerValue];
1749 NSArray *o_array = [NSArray array];
1750 NSArray *o_values = [[o_pasteboard propertyListForType:
1751 NSFilenamesPboardType]
1752 sortedArrayUsingSelector:
1753 @selector(caseInsensitiveCompare:)];
1755 for( i = 0; i < (int)[o_values count]; i++)
1757 NSDictionary *o_dic;
1758 o_dic = [NSDictionary dictionaryWithObject:[o_values
1759 objectAtIndex:i] forKey:@"ITEM_URL"];
1760 o_array = [o_array arrayByAddingObject: o_dic];
1765 [self appendArray: o_array atPos: index enqueue: YES];
1767 else if( p_node->i_children == -1 )
1770 playlist_item_t *p_real_node = NULL;
1772 for( i_counter = 0 ; i_counter < p_node->i_parents ; i_counter++ )
1774 if( p_node->pp_parents[i_counter]->i_view == i_current_view )
1776 p_real_node = p_node->pp_parents[i_counter]->p_parent;
1779 if( i_counter == p_node->i_parents )
1781 vlc_object_release(p_playlist);
1785 [self appendNodeArray: o_array inNode: p_real_node
1786 atPos: index inView: i_current_view enqueue: YES];
1790 [self appendNodeArray: o_array inNode: p_node
1791 atPos: index inView: i_current_view enqueue: YES];
1793 vlc_object_release( p_playlist );
1796 vlc_object_release( p_playlist );
1800 /* Delegate method of NSWindow */
1801 /*- (void)windowWillClose:(NSNotification *)aNotification
1803 [o_btn_playlist setState: NSOffState];