Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

playlist.m

00001 /*****************************************************************************
00002  * playlist.m: MacOS X interface module
00003  *****************************************************************************
00004 * Copyright (C) 2002-2005 the VideoLAN team
00005  * $Id: playlist.m 13288 2005-11-20 16:07:54Z bigben $
00006  *
00007  * Authors: Jon Lech Johansen <[email protected]>
00008  *          Derk-Jan Hartman <hartman at videola/n dot org>
00009  *          Benjamin Pracht <bigben at videolab dot org>
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00024  *****************************************************************************/
00025 
00026 /* TODO
00027  * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
00028  * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
00029  * create toggle buttons for the shuffle, repeat one, repeat all functions.
00030  * implement drag and drop and item reordering.
00031  * reimplement enable/disable item
00032  * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
00033    (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
00034  */
00035 
00036 
00037 /*****************************************************************************
00038  * Preamble
00039  *****************************************************************************/
00040 #include <stdlib.h>                                      /* malloc(), free() */
00041 #include <sys/param.h>                                    /* for MAXPATHLEN */
00042 #include <string.h>
00043 #include <math.h>
00044 #include <sys/mount.h>
00045 #include <vlc_keys.h>
00046 
00047 #include "intf.h"
00048 #import "wizard.h"
00049 #import "bookmarks.h"
00050 #include "playlist.h"
00051 #include "controls.h"
00052 #include "vlc_osd.h"
00053 #include "misc.h"
00054 
00055 /*****************************************************************************
00056  * VLCPlaylistView implementation 
00057  *****************************************************************************/
00058 @implementation VLCPlaylistView
00059 
00060 - (NSMenu *)menuForEvent:(NSEvent *)o_event
00061 {
00062     return( [[self delegate] menuForEvent: o_event] );
00063 }
00064 
00065 - (void)keyDown:(NSEvent *)o_event
00066 {
00067     unichar key = 0;
00068 
00069     if( [[o_event characters] length] )
00070     {
00071         key = [[o_event characters] characterAtIndex: 0];
00072     }
00073 
00074     switch( key )
00075     {
00076         case NSDeleteCharacter:
00077         case NSDeleteFunctionKey:
00078         case NSDeleteCharFunctionKey:
00079         case NSBackspaceCharacter:
00080             [[self delegate] deleteItem:self];
00081             break;
00082 
00083         case NSEnterCharacter:
00084         case NSCarriageReturnCharacter:
00085             [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist]
00086                                                             playItem:self];
00087             break;
00088 
00089         default:
00090             [super keyDown: o_event];
00091             break;
00092     }
00093 }
00094 
00095 @end
00096 
00097 
00098 /*****************************************************************************
00099  * VLCPlaylistCommon implementation
00100  *
00101  * This class the superclass of the VLCPlaylist and VLCPlaylistWizard.
00102  * It contains the common methods and elements of these 2 entities.
00103  *****************************************************************************/
00104 @implementation VLCPlaylistCommon
00105 
00106 - (id)init
00107 {
00108     self = [super init];
00109     if ( self != nil )
00110     {
00111         o_outline_dict = [[NSMutableDictionary alloc] init];
00112     }
00113     return self;
00114 }        
00115 - (void)awakeFromNib
00116 {
00117     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00118                                           FIND_ANYWHERE );
00119     i_current_view = VIEW_CATEGORY;
00120     playlist_ViewUpdate( p_playlist, i_current_view );
00121 
00122     [o_outline_view setTarget: self];
00123     [o_outline_view setDelegate: self];
00124     [o_outline_view setDataSource: self];
00125 
00126     vlc_object_release( p_playlist );
00127     [self initStrings];
00128 }
00129 
00130 - (void)initStrings
00131 {
00132     [[o_tc_name headerCell] setStringValue:_NS("Name")];
00133     [[o_tc_author headerCell] setStringValue:_NS("Author")];
00134     [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
00135 }
00136 
00137 - (NSOutlineView *)outlineView
00138 {
00139     return o_outline_view;
00140 }
00141 
00142 - (playlist_item_t *)selectedPlaylistItem
00143 {
00144     return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
00145                                                                 pointerValue];
00146 }
00147 
00148 @end
00149 
00150 @implementation VLCPlaylistCommon (NSOutlineViewDataSource)
00151 
00152 /* return the number of children for Obj-C pointer item */ /* DONE */
00153 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
00154 {
00155     int i_return = 0;
00156     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00157                                                        FIND_ANYWHERE );
00158     if( p_playlist == NULL )
00159         return 0;
00160     if( outlineView != o_outline_view )
00161     {
00162         vlc_object_release( p_playlist );
00163         return 0;
00164     }
00165 
00166     if( item == nil )
00167     {
00168         /* root object */
00169         playlist_view_t *p_view;
00170         p_view = playlist_ViewFind( p_playlist, i_current_view );
00171         if( p_view && p_view->p_root )
00172         {
00173             i_return = p_view->p_root->i_children;
00174 
00175             if( i_current_view == VIEW_CATEGORY )
00176             {
00177                 i_return--; /* remove the GENERAL item from the list */
00178                 i_return += p_playlist->p_general->i_children; /* add the items of the general node */
00179             }
00180         }
00181     }
00182     else
00183     {
00184         playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
00185         if( p_item )
00186             i_return = p_item->i_children;
00187     }
00188     vlc_object_release( p_playlist );
00189     
00190     if( i_return <= 0 )
00191         i_return = 0;
00192     
00193     return i_return;
00194 }
00195 
00196 /* return the child at index for the Obj-C pointer item */ /* DONE */
00197 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
00198 {
00199     playlist_item_t *p_return = NULL;
00200     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00201                                                        FIND_ANYWHERE );
00202     NSValue *o_value;
00203 
00204     if( p_playlist == NULL )
00205         return nil;
00206 
00207     if( item == nil )
00208     {
00209         /* root object */
00210         playlist_view_t *p_view;
00211         p_view = playlist_ViewFind( p_playlist, i_current_view );
00212         if( p_view && p_view->p_root ) p_return = p_view->p_root->pp_children[index];
00213 
00214         if( i_current_view == VIEW_CATEGORY )
00215         {
00216             if( p_playlist->p_general->i_children && index >= 0 && index < p_playlist->p_general->i_children )
00217             {
00218                 p_return = p_playlist->p_general->pp_children[index];
00219             }
00220             else if( p_view && p_view->p_root && index >= 0 && index - p_playlist->p_general->i_children < p_view->p_root->i_children )
00221             {
00222                 p_return = p_view->p_root->pp_children[index - p_playlist->p_general->i_children + 1];
00223             }
00224         }
00225     }
00226     else
00227     {
00228         playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
00229         if( p_item && index < p_item->i_children && index >= 0 )
00230             p_return = p_item->pp_children[index];
00231     }
00232     
00233 
00234     vlc_object_release( p_playlist );
00235 
00236     o_value = [o_outline_dict objectForKey:[NSString stringWithFormat: @"%p", p_return]];
00237     if( o_value == nil )
00238     {
00239         o_value = [[NSValue valueWithPointer: p_return] retain];
00240     }
00241     return o_value;
00242 }
00243 
00244 /* is the item expandable */
00245 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
00246 {
00247     int i_return = 0;
00248     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00249                                                        FIND_ANYWHERE );
00250     if( p_playlist == NULL )
00251         return NO;
00252 
00253     if( item == nil )
00254     {
00255         /* root object */
00256         playlist_view_t *p_view;
00257         p_view = playlist_ViewFind( p_playlist, i_current_view );
00258         if( p_view && p_view->p_root ) i_return = p_view->p_root->i_children;
00259 
00260         if( i_current_view == VIEW_CATEGORY )
00261         {
00262             i_return--;
00263             i_return += p_playlist->p_general->i_children;
00264         }
00265     }
00266     else
00267     {
00268         playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
00269         if( p_item )
00270             i_return = p_item->i_children;
00271     }
00272     vlc_object_release( p_playlist );
00273 
00274     if( i_return <= 0 )
00275         return NO;
00276     else
00277         return YES;
00278 }
00279 
00280 /* retrieve the string values for the cells */
00281 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
00282 {
00283     id o_value = nil;
00284     intf_thread_t *p_intf = VLCIntf;
00285     playlist_t *p_playlist;
00286     playlist_item_t *p_item;
00287     
00288     if( item == nil || ![item isKindOfClass: [NSValue class]] ) return( @"error" );
00289     
00290     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
00291                                                FIND_ANYWHERE );
00292     if( p_playlist == NULL )
00293     {
00294         return( @"error" );
00295     }
00296 
00297     p_item = (playlist_item_t *)[item pointerValue];
00298 
00299     if( p_item == NULL )
00300     {
00301         vlc_object_release( p_playlist );
00302         return( @"error");
00303     }
00304 
00305     if( [[o_tc identifier] isEqualToString:@"1"] )
00306     {
00307         o_value = [NSString stringWithUTF8String:
00308             p_item->input.psz_name];
00309         if( o_value == NULL )
00310             o_value = [NSString stringWithCString:
00311                 p_item->input.psz_name];
00312     }
00313     else if( [[o_tc identifier] isEqualToString:@"2"] )
00314     {
00315         char *psz_temp;
00316         psz_temp = vlc_input_item_GetInfo( &p_item->input ,_("Meta-information"),_("Artist") );
00317 
00318         if( psz_temp == NULL )
00319             o_value = @"";
00320         else
00321         {
00322             o_value = [NSString stringWithUTF8String: psz_temp];
00323             if( o_value == NULL )
00324             {
00325                 o_value = [NSString stringWithCString: psz_temp];
00326             }
00327             free( psz_temp );
00328         }
00329     }
00330     else if( [[o_tc identifier] isEqualToString:@"3"] )
00331     {
00332         char psz_duration[MSTRTIME_MAX_SIZE];
00333         mtime_t dur = p_item->input.i_duration;
00334         if( dur != -1 )
00335         {
00336             secstotimestr( psz_duration, dur/1000000 );
00337             o_value = [NSString stringWithUTF8String: psz_duration];
00338         }
00339         else
00340         {
00341             o_value = @"-:--:--";
00342         }
00343     }
00344     vlc_object_release( p_playlist );
00345 
00346     return( o_value );
00347 }
00348 
00349 @end
00350 
00351 /*****************************************************************************
00352  * VLCPlaylistWizard implementation
00353  *****************************************************************************/
00354 @implementation VLCPlaylistWizard
00355 
00356 - (IBAction)reloadOutlineView
00357 {
00358     /* Only reload the outlineview if the wizard window is open since this can
00359        be quite long on big playlists */
00360     if( [[o_outline_view window] isVisible] )
00361     {
00362         [o_outline_view reloadData];
00363     }
00364 }
00365 
00366 @end
00367 
00368 /*****************************************************************************
00369  * VLCPlaylist implementation
00370  *****************************************************************************/
00371 @implementation VLCPlaylist
00372 
00373 - (id)init
00374 {
00375     self = [super init];
00376     if ( self != nil )
00377     {
00378         o_nodes_array = [[NSMutableArray alloc] init];
00379         o_items_array = [[NSMutableArray alloc] init];
00380     }
00381     return self;
00382 }
00383 
00384 - (void)awakeFromNib
00385 {
00386     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00387                                           FIND_ANYWHERE );
00388     vlc_list_t *p_list = vlc_list_find( p_playlist, VLC_OBJECT_MODULE,
00389                                         FIND_ANYWHERE );
00390 
00391     int i_index;
00392 
00393     [super awakeFromNib];
00394 
00395     [o_outline_view setDoubleAction: @selector(playItem:)];
00396 
00397     [o_outline_view registerForDraggedTypes:
00398         [NSArray arrayWithObjects: NSFilenamesPboardType,
00399         @"VLCPlaylistItemPboardType", nil]];
00400     [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
00401 
00402 /* We need to check whether _defaultTableHeaderSortImage exists, since it 
00403 belongs to an Apple hidden private API, and then can "disapear" at any time*/
00404 
00405     if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
00406     {
00407         o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
00408     }
00409     else
00410     {
00411         o_ascendingSortingImage = nil;
00412     }
00413 
00414     if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
00415     {
00416         o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
00417     }
00418     else
00419     {
00420         o_descendingSortingImage = nil;
00421     }
00422 
00423     o_tc_sortColumn = nil;
00424 
00425     for( i_index = 0; i_index < p_list->i_count; i_index++ )
00426     {
00427         NSMenuItem * o_lmi;
00428         module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
00429 
00430         if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
00431         {
00432             /* create the menu entries used in the playlist menu */
00433             o_lmi = [[o_mi_services submenu] addItemWithTitle:
00434                      [NSString stringWithUTF8String:
00435                      p_parser->psz_longname ? p_parser->psz_longname :
00436                      ( p_parser->psz_shortname ? p_parser->psz_shortname:
00437                      p_parser->psz_object_name)]
00438                                              action: @selector(servicesChange:)
00439                                              keyEquivalent: @""];
00440             [o_lmi setTarget: self];
00441             [o_lmi setRepresentedObject:
00442                    [NSString stringWithCString: p_parser->psz_object_name]];
00443             if( playlist_IsServicesDiscoveryLoaded( p_playlist,
00444                     p_parser->psz_object_name ) )
00445                 [o_lmi setState: NSOnState];
00446                 
00447             /* create the menu entries for the main menu */
00448             o_lmi = [[o_mm_mi_services submenu] addItemWithTitle:
00449                      [NSString stringWithUTF8String:
00450                      p_parser->psz_longname ? p_parser->psz_longname :
00451                      ( p_parser->psz_shortname ? p_parser->psz_shortname:
00452                      p_parser->psz_object_name)]
00453                                              action: @selector(servicesChange:)
00454                                              keyEquivalent: @""];
00455             [o_lmi setTarget: self];
00456             [o_lmi setRepresentedObject:
00457                    [NSString stringWithCString: p_parser->psz_object_name]];
00458             if( playlist_IsServicesDiscoveryLoaded( p_playlist,
00459                     p_parser->psz_object_name ) )
00460                 [o_lmi setState: NSOnState];
00461         }
00462     }
00463     vlc_list_release( p_list );
00464     vlc_object_release( p_playlist );
00465 
00466     /* Change the simple textfield into a searchField if we can... */
00467 #if 0
00468     if( MACOS_VERSION >= 10.3 )
00469     {
00470         NSView *o_parentview = [o_status_field superview];
00471         NSSearchField *o_better_search_field = [[NSSearchField alloc]initWithFrame:[o_search_field frame]];
00472         [o_better_search_field setRecentsAutosaveName:@"VLC media player search"];
00473         [o_better_search_field setDelegate:self];
00474         [[NSNotificationCenter defaultCenter] addObserver: self
00475             selector: @selector(searchfieldChanged:)
00476             name: NSControlTextDidChangeNotification
00477             object: o_better_search_field];
00478 
00479         [o_better_search_field setTarget:self];
00480         [o_better_search_field setAction:@selector(searchItem:)];
00481 
00482         [o_better_search_field setAutoresizingMask:NSViewMinXMargin];
00483         [o_parentview addSubview:o_better_search_field];
00484         [o_search_field setHidden:YES];
00485     }
00486 #endif
00487     //[self playlistUpdated];
00488 }
00489 
00490 - (void)searchfieldChanged:(NSNotification *)o_notification
00491 {
00492     [o_search_field setStringValue:[[o_notification object] stringValue]];
00493 }
00494 
00495 - (void)initStrings
00496 {
00497     [super initStrings];
00498 
00499     [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
00500     [o_mi_play setTitle: _NS("Play")];
00501     [o_mi_delete setTitle: _NS("Delete")];
00502     [o_mi_recursive_expand setTitle: _NS("Expand Node")];
00503     [o_mi_selectall setTitle: _NS("Select All")];
00504     [o_mi_info setTitle: _NS("Properties")];
00505     [o_mi_preparse setTitle: _NS("Preparse")];
00506     [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
00507     [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
00508     [o_mi_services setTitle: _NS("Services discovery")];
00509     [o_status_field setStringValue: [NSString stringWithFormat:
00510                         _NS("No items in the playlist")]];
00511 
00512     [o_random_ckb setTitle: _NS("Random")];
00513 #if 0
00514     [o_search_button setTitle: _NS("Search")];
00515 #endif
00516     [o_search_field setToolTip: _NS("Search in Playlist")];
00517     [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
00518     [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
00519     [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
00520 }
00521 
00522 - (void)playlistUpdated
00523 {
00524     unsigned int i;
00525 
00526     /* Clear indications of any existing column sorting*/
00527     for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
00528     {
00529         [o_outline_view setIndicatorImage:nil inTableColumn:
00530                             [[o_outline_view tableColumns] objectAtIndex:i]];
00531     }
00532 
00533     [o_outline_view setHighlightedTableColumn:nil];
00534     o_tc_sortColumn = nil;
00535     // TODO Find a way to keep the dict size to a minimum
00536     //[o_outline_dict removeAllObjects];
00537     [o_outline_view reloadData];
00538     [[[[VLCMain sharedInstance] getWizard] getPlaylistWizard] reloadOutlineView];
00539     [[[[VLCMain sharedInstance] getBookmarks] getDataTable] reloadData];
00540 }
00541 
00542 - (void)playModeUpdated
00543 {
00544     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00545                                           FIND_ANYWHERE );
00546     vlc_value_t val, val2;
00547 
00548     if( p_playlist == NULL )
00549     {
00550         return;
00551     }
00552 
00553     var_Get( p_playlist, "loop", &val2 );
00554     var_Get( p_playlist, "repeat", &val );
00555     if( val.b_bool == VLC_TRUE )
00556     {
00557         [o_loop_popup selectItemAtIndex: 1];
00558    }
00559     else if( val2.b_bool == VLC_TRUE )
00560     {
00561         [o_loop_popup selectItemAtIndex: 2];
00562     }
00563     else
00564     {
00565         [o_loop_popup selectItemAtIndex: 0];
00566     }
00567 
00568     var_Get( p_playlist, "random", &val );
00569     [o_random_ckb setState: val.b_bool];
00570 
00571     vlc_object_release( p_playlist );
00572 }
00573 
00574 - (playlist_item_t *)parentOfItem:(playlist_item_t *)p_item
00575 {
00576     int i;
00577     for( i = 0 ; i < p_item->i_parents; i++ )
00578     {
00579         if( p_item->pp_parents[i]->i_view == i_current_view )
00580         {
00581             return p_item->pp_parents[i]->p_parent;
00582         }
00583     }
00584     return NULL;
00585 }
00586 
00587 - (void)updateRowSelection
00588 {
00589     int i_row;
00590     unsigned int j;
00591 
00592     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00593                                           FIND_ANYWHERE );
00594     playlist_item_t *p_item, *p_temp_item;
00595     NSMutableArray *o_array = [NSMutableArray array];
00596 
00597     if( p_playlist == NULL )
00598         return;
00599 
00600     p_item = p_playlist->status.p_item;
00601     if( p_item == NULL )
00602     {
00603         vlc_object_release(p_playlist);
00604         return;
00605     }
00606 
00607     p_temp_item = p_item;
00608     while( p_temp_item->i_parents > 0 )
00609     {
00610         [o_array insertObject: [NSValue valueWithPointer: p_temp_item] atIndex: 0];
00611 
00612         p_temp_item = [self parentOfItem: p_temp_item];
00613         /*for (i = 0 ; i < p_temp_item->i_parents ; i++)
00614         {
00615             if( p_temp_item->pp_parents[i]->i_view == i_current_view )
00616             {
00617                 p_temp_item = p_temp_item->pp_parents[i]->p_parent;
00618                 break;
00619             }
00620         }*/
00621     }
00622 
00623     for (j = 0 ; j < [o_array count] - 1 ; j++)
00624     {
00625         id o_item;
00626         if( ( o_item = [o_outline_dict objectForKey:
00627                             [NSString stringWithFormat: @"%p",
00628                             [[o_array objectAtIndex:j] pointerValue]]] ) != nil )
00629             [o_outline_view expandItem: o_item];
00630 
00631     }
00632 
00633     i_row = [o_outline_view rowForItem:[o_outline_dict
00634             objectForKey:[NSString stringWithFormat: @"%p", p_item]]];
00635 
00636     [o_outline_view selectRow: i_row byExtendingSelection: NO];
00637     [o_outline_view scrollRowToVisible: i_row];
00638 
00639     vlc_object_release(p_playlist);
00640 }
00641 
00642 /* Check if p_item is a child of p_node recursively. We need to check the item
00643    existence first since OSX sometimes tries to redraw items that have been
00644    deleted. We don't do it when not required  since this verification takes
00645    quite a long time on big playlists (yes, pretty hacky). */
00646 - (BOOL)isItem: (playlist_item_t *)p_item
00647                     inNode: (playlist_item_t *)p_node
00648                     checkItemExistence:(BOOL)b_check
00649 
00650 {
00651     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00652                                           FIND_ANYWHERE );
00653     playlist_item_t *p_temp_item = p_item;
00654 
00655     if( p_playlist == NULL )
00656     {
00657         return NO;
00658     }
00659 
00660     if( p_node == p_item )
00661     {
00662         vlc_object_release(p_playlist);
00663         return YES;
00664     }
00665 
00666     if( p_node->i_children < 1)
00667     {
00668         vlc_object_release(p_playlist);
00669         return NO;
00670     }
00671 
00672     if ( p_temp_item )
00673     {
00674         int i;
00675         vlc_mutex_lock( &p_playlist->object_lock );
00676 
00677         if( b_check )
00678         {
00679         /* Since outlineView: willDisplayCell:... may call this function with
00680            p_items that don't exist anymore, first check if the item is still
00681            in the playlist. Any cleaner solution welcomed. */
00682             for( i = 0; i < p_playlist->i_all_size; i++ )
00683             {
00684                 if( p_playlist->pp_all_items[i] == p_item ) break;
00685                 else if ( i == p_playlist->i_all_size - 1 )
00686                 {
00687                     vlc_object_release( p_playlist );
00688                     vlc_mutex_unlock( &p_playlist->object_lock );
00689                     return NO;
00690                 }
00691             }
00692         }
00693 
00694         while( p_temp_item->i_parents > 0 )
00695         {
00696             p_temp_item = [self parentOfItem: p_temp_item];
00697             if( p_temp_item == p_node )
00698             {
00699                  vlc_mutex_unlock( &p_playlist->object_lock );
00700                  vlc_object_release( p_playlist );
00701                  return YES;
00702             }
00703 
00704 /*            for( i = 0; i < p_temp_item->i_parents ; i++ )
00705             {
00706                 if( p_temp_item->pp_parents[i]->i_view == i_current_view )
00707                 {
00708                     if( p_temp_item->pp_parents[i]->p_parent == p_node )
00709                     {
00710                         vlc_mutex_unlock( &p_playlist->object_lock );
00711                         vlc_object_release( p_playlist );
00712                         return YES;
00713                     }
00714                     else
00715                     {
00716                         p_temp_item = p_temp_item->pp_parents[i]->p_parent;
00717                         break;
00718                     }
00719                 }
00720             }*/
00721         }
00722         vlc_mutex_unlock( &p_playlist->object_lock );
00723     }
00724 
00725     vlc_object_release( p_playlist );
00726     return NO;
00727 }
00728 
00729 /* This method is usefull for instance to remove the selected children of an
00730    already selected node */
00731 - (void)removeItemsFrom:(id)o_items ifChildrenOf:(id)o_nodes
00732 {
00733     unsigned int i, j;
00734     for( i = 0 ; i < [o_items count] ; i++ )
00735     {
00736         for ( j = 0 ; j < [o_nodes count] ; j++ )
00737         {
00738             if( o_items == o_nodes)
00739             {
00740                 if( j == i ) continue;
00741             }
00742             if( [self isItem: [[o_items objectAtIndex:i] pointerValue]
00743                     inNode: [[o_nodes objectAtIndex:j] pointerValue]
00744                     checkItemExistence: NO] )
00745             {
00746                 [o_items removeObjectAtIndex:i];
00747                 /* We need to execute the next iteration with the same index
00748                    since the current item has been deleted */
00749                 i--;
00750                 break;
00751             }
00752         }
00753     }
00754 
00755 }
00756 
00757 - (IBAction)savePlaylist:(id)sender
00758 {
00759     intf_thread_t * p_intf = VLCIntf;
00760     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
00761                                                        FIND_ANYWHERE );
00762 
00763     NSSavePanel *o_save_panel = [NSSavePanel savePanel];
00764     NSString * o_name = [NSString stringWithFormat: @"%@.m3u", _NS("Untitled")];
00765     [o_save_panel setTitle: _NS("Save Playlist")];
00766     [o_save_panel setPrompt: _NS("Save")];
00767 
00768     if( [o_save_panel runModalForDirectory: nil
00769             file: o_name] == NSOKButton )
00770     {
00771         playlist_Export( p_playlist, [[o_save_panel filename] fileSystemRepresentation], "export-m3u" );
00772     }
00773     vlc_object_release( p_playlist );
00774 }
00775 
00776 
00777 /* When called retrieves the selected outlineview row and plays that node or item */
00778 - (IBAction)playItem:(id)sender
00779 {
00780     intf_thread_t * p_intf = VLCIntf;
00781     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
00782                                                        FIND_ANYWHERE );
00783 
00784     if( p_playlist != NULL )
00785     {
00786         playlist_item_t *p_item;
00787         playlist_item_t *p_node = NULL;
00788 
00789         p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
00790 
00791         if( p_item )
00792         {
00793             if( p_item->i_children == -1 )
00794             {
00795                 p_node = [self parentOfItem: p_item];
00796 
00797 /*                for( i = 0 ; i < p_item->i_parents ; i++ )
00798                 {
00799                     if( p_item->pp_parents[i]->i_view == i_current_view )
00800                     {
00801                         p_node = p_item->pp_parents[i]->p_parent;
00802                     }
00803                 }*/
00804             }
00805             else
00806             {
00807                 p_node = p_item;
00808                 if( p_node->i_children > 0 && p_node->pp_children[0]->i_children == -1 )
00809                 {
00810                     p_item = p_node->pp_children[0];
00811                 }
00812                 else
00813                 {
00814                     p_item = NULL;
00815                 }
00816             }
00817             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, i_current_view, p_node, p_item );
00818         }
00819         vlc_object_release( p_playlist );
00820     }
00821 }
00822 
00823 /* When called retrieves the selected outlineview row and plays that node or item */
00824 - (IBAction)preparseItem:(id)sender
00825 {
00826     int i_count;
00827     NSMutableArray *o_to_preparse;
00828     intf_thread_t * p_intf = VLCIntf;
00829     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
00830                                                        FIND_ANYWHERE );
00831                                                        
00832     o_to_preparse = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
00833     i_count = [o_to_preparse count];
00834 
00835     if( p_playlist != NULL )
00836     {
00837         int i, i_row;
00838         NSNumber *o_number;
00839         playlist_item_t *p_item = NULL;
00840 
00841         for( i = 0; i < i_count; i++ )
00842         {
00843             o_number = [o_to_preparse lastObject];
00844             i_row = [o_number intValue];
00845             p_item = [[o_outline_view itemAtRow:i_row] pointerValue];
00846             [o_to_preparse removeObject: o_number];
00847             [o_outline_view deselectRow: i_row];
00848 
00849             if( p_item )
00850             {
00851                 if( p_item->i_children == -1 )
00852                 {
00853                     playlist_PreparseEnqueue( p_playlist, &p_item->input );
00854                 }
00855                 else
00856                 {
00857                     msg_Dbg( p_intf, "preparse of nodes not yet implemented" );
00858                 }
00859             }
00860         }
00861         vlc_object_release( p_playlist );
00862     }
00863     [self playlistUpdated];
00864 }
00865 
00866 - (IBAction)servicesChange:(id)sender
00867 {
00868     NSMenuItem *o_mi = (NSMenuItem *)sender;
00869     NSString *o_string = [o_mi representedObject];
00870     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00871                                           FIND_ANYWHERE );
00872     if( !playlist_IsServicesDiscoveryLoaded( p_playlist, [o_string cString] ) )
00873         playlist_ServicesDiscoveryAdd( p_playlist, [o_string cString] );
00874     else
00875         playlist_ServicesDiscoveryRemove( p_playlist, [o_string cString] );
00876 
00877     [o_mi setState: playlist_IsServicesDiscoveryLoaded( p_playlist,
00878                                           [o_string cString] ) ? YES : NO];
00879 
00880     i_current_view = VIEW_CATEGORY;
00881     playlist_ViewUpdate( p_playlist, i_current_view );
00882     vlc_object_release( p_playlist );
00883     [self playlistUpdated];
00884     return;
00885 }
00886 
00887 - (IBAction)selectAll:(id)sender
00888 {
00889     [o_outline_view selectAll: nil];
00890 }
00891 
00892 - (IBAction)deleteItem:(id)sender
00893 {
00894     int i, i_count, i_row;
00895     NSMutableArray *o_to_delete;
00896     NSNumber *o_number;
00897 
00898     playlist_t * p_playlist;
00899     intf_thread_t * p_intf = VLCIntf;
00900 
00901     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
00902                                           FIND_ANYWHERE );
00903 
00904     if ( p_playlist == NULL )
00905     {
00906         return;
00907     }
00908     o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
00909     i_count = [o_to_delete count];
00910 
00911     for( i = 0; i < i_count; i++ )
00912     {
00913         o_number = [o_to_delete lastObject];
00914         i_row = [o_number intValue];
00915         id o_item = [o_outline_view itemAtRow: i_row];
00916         playlist_item_t *p_item = [o_item pointerValue];
00917         [o_to_delete removeObject: o_number];
00918         [o_outline_view deselectRow: i_row];
00919 
00920         if( [[o_outline_view dataSource] outlineView:o_outline_view
00921                                         numberOfChildrenOfItem: o_item]  > 0 )
00922         //is a node and not an item
00923         {
00924             if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
00925                 [self isItem: p_playlist->status.p_item inNode:
00926                         ((playlist_item_t *)[o_item pointerValue])
00927                         checkItemExistence: NO] == YES )
00928             {
00929                 // if current item is in selected node and is playing then stop playlist
00930                 playlist_Stop( p_playlist );
00931             }
00932             vlc_mutex_lock( &p_playlist->object_lock );
00933             playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE );
00934             vlc_mutex_unlock( &p_playlist->object_lock );
00935         }
00936         else
00937         {
00938             if( p_playlist->status.i_status != PLAYLIST_STOPPED &&
00939                 p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row] pointerValue] )
00940             {
00941                 playlist_Stop( p_playlist );
00942             }
00943             vlc_mutex_lock( &p_playlist->object_lock );
00944             playlist_Delete( p_playlist, p_item->input.i_id );
00945             vlc_mutex_unlock( &p_playlist->object_lock );
00946         }
00947     }
00948     [self playlistUpdated];
00949     vlc_object_release( p_playlist );
00950 }
00951 
00952 - (IBAction)sortNodeByName:(id)sender
00953 {
00954     [self sortNode: SORT_TITLE];
00955 }
00956 
00957 - (IBAction)sortNodeByAuthor:(id)sender
00958 {
00959     [self sortNode: SORT_AUTHOR];
00960 }
00961 
00962 - (void)sortNode:(int)i_mode
00963 {
00964     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
00965                                           FIND_ANYWHERE );
00966     playlist_item_t * p_item;
00967 
00968     if (p_playlist == NULL)
00969     {
00970         return;
00971     }
00972 
00973     if( [o_outline_view selectedRow] > -1 )
00974     {
00975         p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
00976                                                                 pointerValue];
00977     }
00978     else
00979     /*If no item is selected, sort the whole playlist*/
00980     {
00981         playlist_view_t * p_view = playlist_ViewFind( p_playlist, i_current_view );
00982         p_item = p_view->p_root;
00983     }
00984 
00985     if( p_item->i_children > -1 ) // the item is a node
00986     {
00987         vlc_mutex_lock( &p_playlist->object_lock );
00988         playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
00989         vlc_mutex_unlock( &p_playlist->object_lock );
00990     }
00991     else
00992     {
00993         int i;
00994 
00995         for( i = 0 ; i < p_item->i_parents ; i++ )
00996         {
00997             if( p_item->pp_parents[i]->i_view == i_current_view )
00998             {
00999                 vlc_mutex_lock( &p_playlist->object_lock );
01000                 playlist_RecursiveNodeSort( p_playlist,
01001                         p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
01002                 vlc_mutex_unlock( &p_playlist->object_lock );
01003                 break;
01004             }
01005         }
01006     }
01007     vlc_object_release( p_playlist );
01008     [self playlistUpdated];
01009 }
01010 
01011 - (playlist_item_t *)createItem:(NSDictionary *)o_one_item
01012 {
01013     intf_thread_t * p_intf = VLCIntf;
01014     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
01015                                                        FIND_ANYWHERE );
01016 
01017     if( p_playlist == NULL )
01018     {
01019         return NULL;
01020     }
01021     playlist_item_t *p_item;
01022     int i;
01023     BOOL b_rem = FALSE, b_dir = FALSE;
01024     NSString *o_uri, *o_name;
01025     NSArray *o_options;
01026     NSURL *o_true_file;
01027 
01028     /* Get the item */
01029     o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
01030     o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
01031     o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
01032 
01033     /* Find the name for a disc entry ( i know, can you believe the trouble?) */
01034     if( ( !o_name || [o_name isEqualToString:@""] ) && [o_uri rangeOfString: @"/dev/"].location != NSNotFound )
01035     {
01036         int i_count, i_index;
01037         struct statfs *mounts = NULL;
01038 
01039         i_count = getmntinfo (&mounts, MNT_NOWAIT);
01040         /* getmntinfo returns a pointer to static data. Do not free. */
01041         for( i_index = 0 ; i_index < i_count; i_index++ )
01042         {
01043             NSMutableString *o_temp, *o_temp2;
01044             o_temp = [NSMutableString stringWithString: o_uri];
01045             o_temp2 = [NSMutableString stringWithCString: mounts[i_index].f_mntfromname];
01046             [o_temp replaceOccurrencesOfString: @"/dev/rdisk" withString: @"/dev/disk" options:nil range:NSMakeRange(0, [o_temp length]) ];
01047             [o_temp2 replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
01048             [o_temp2 replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp2 length]) ];
01049 
01050             if( strstr( [o_temp fileSystemRepresentation], [o_temp2 fileSystemRepresentation] ) != NULL )
01051             {
01052                 o_name = [[NSFileManager defaultManager] displayNameAtPath: [NSString stringWithCString:mounts[i_index].f_mntonname]];
01053             }
01054         }
01055     }
01056     /* If no name, then make a guess */
01057     if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
01058 
01059     if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
01060         [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
01061                 isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem   )
01062     {
01063         /* All of this is to make sure CD's play when you D&D them on VLC */
01064         /* Converts mountpoint to a /dev file */
01065         struct statfs *buf;
01066         char *psz_dev;
01067         NSMutableString *o_temp;
01068 
01069         buf = (struct statfs *) malloc (sizeof(struct statfs));
01070         statfs( [o_uri fileSystemRepresentation], buf );
01071         psz_dev = strdup(buf->f_mntfromname);
01072         o_temp = [NSMutableString stringWithCString: psz_dev ];
01073         [o_temp replaceOccurrencesOfString: @"/dev/disk" withString: @"/dev/rdisk" options:nil range:NSMakeRange(0, [o_temp length]) ];
01074         [o_temp replaceOccurrencesOfString: @"s0" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
01075         [o_temp replaceOccurrencesOfString: @"s1" withString: @"" options:nil range:NSMakeRange(0, [o_temp length]) ];
01076         o_uri = o_temp;
01077     }
01078 
01079     p_item = playlist_ItemNew( p_intf, [o_uri fileSystemRepresentation], [o_name UTF8String] );
01080     if( !p_item )
01081        return NULL;
01082 
01083     if( o_options )
01084     {
01085         for( i = 0; i < (int)[o_options count]; i++ )
01086         {
01087             playlist_ItemAddOption( p_item, strdup( [[o_options objectAtIndex:i] UTF8String] ) );
01088         }
01089     }
01090 
01091     /* Recent documents menu */
01092     o_true_file = [NSURL fileURLWithPath: o_uri];
01093     if( o_true_file != nil )
01094     {
01095         [[NSDocumentController sharedDocumentController]
01096             noteNewRecentDocumentURL: o_true_file];
01097     }
01098 
01099     vlc_object_release( p_playlist );
01100     return p_item;
01101 }
01102 
01103 - (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
01104 {
01105     int i_item;
01106     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01107                                             FIND_ANYWHERE );
01108     if( p_playlist == NULL )
01109     {
01110         return;
01111     }
01112 
01113     for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
01114     {
01115         playlist_item_t *p_item;
01116         NSDictionary *o_one_item;
01117 
01118         /* Get the item */
01119         o_one_item = [o_array objectAtIndex: i_item];
01120         p_item = [self createItem: o_one_item];
01121         if( !p_item )
01122         {
01123             continue;
01124         }
01125 
01126         /* Add the item */
01127         playlist_AddItem( p_playlist, p_item, PLAYLIST_INSERT, i_position == -1 ? PLAYLIST_END : i_position + i_item );
01128 
01129         if( i_item == 0 && !b_enqueue )
01130         {
01131             playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
01132         }
01133     }
01134     vlc_object_release( p_playlist );
01135 }
01136 
01137 - (void)appendNodeArray:(NSArray*)o_array inNode:(playlist_item_t *)p_node atPos:(int)i_position inView:(int)i_view enqueue:(BOOL)b_enqueue
01138 {
01139     int i_item;
01140     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01141                                             FIND_ANYWHERE );
01142     if( p_playlist == NULL )
01143     {
01144         return;
01145     }
01146 
01147     for( i_item = 0; i_item < (int)[o_array count]; i_item++ )
01148     {
01149         playlist_item_t *p_item;
01150         NSDictionary *o_one_item;
01151 
01152         /* Get the item */
01153         o_one_item = [o_array objectAtIndex: i_item];
01154         p_item = [self createItem: o_one_item];
01155         if( !p_item )
01156         {
01157             continue;
01158         }
01159 
01160         /* Add the item */
01161         playlist_NodeAddItem( p_playlist, p_item, i_view, p_node, PLAYLIST_INSERT, i_position + i_item );
01162 
01163         if( i_item == 0 && !b_enqueue )
01164         {
01165             playlist_Control( p_playlist, PLAYLIST_ITEMPLAY, p_item );
01166         }
01167     }
01168     vlc_object_release( p_playlist );
01169 
01170 }
01171 
01172 - (IBAction)handlePopUp:(id)sender
01173 
01174 {
01175     intf_thread_t * p_intf = VLCIntf;
01176     vlc_value_t val1,val2;
01177     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
01178                                             FIND_ANYWHERE );
01179     if( p_playlist == NULL )
01180     {
01181         return;
01182     }
01183 
01184     switch( [o_loop_popup indexOfSelectedItem] )
01185     {
01186         case 1:
01187 
01188              val1.b_bool = 0;
01189              var_Set( p_playlist, "loop", val1 );
01190              val1.b_bool = 1;
01191              var_Set( p_playlist, "repeat", val1 );
01192              vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
01193         break;
01194 
01195         case 2:
01196              val1.b_bool = 0;
01197              var_Set( p_playlist, "repeat", val1 );
01198              val1.b_bool = 1;
01199              var_Set( p_playlist, "loop", val1 );
01200              vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
01201         break;
01202 
01203         default:
01204              var_Get( p_playlist, "repeat", &val1 );
01205              var_Get( p_playlist, "loop", &val2 );
01206              if( val1.b_bool || val2.b_bool )
01207              {
01208                   val1.b_bool = 0;
01209                   var_Set( p_playlist, "repeat", val1 );
01210                   var_Set( p_playlist, "loop", val1 );
01211                   vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
01212              }
01213          break;
01214      }
01215      vlc_object_release( p_playlist );
01216      [self playlistUpdated];
01217 }
01218 
01219 - (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
01220 {
01221     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01222                                                        FIND_ANYWHERE );
01223     playlist_item_t *p_selected_item;
01224     int i_current, i_selected_row;
01225 
01226     if( !p_playlist )
01227         return NULL;
01228 
01229     i_selected_row = [o_outline_view selectedRow];
01230     if (i_selected_row < 0)
01231         i_selected_row = 0;
01232 
01233     p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
01234                                             i_selected_row] pointerValue];
01235 
01236     for( i_current = 0; i_current < p_item->i_children ; i_current++ )
01237     {
01238         char *psz_temp;
01239         NSString *o_current_name, *o_current_author;
01240 
01241         vlc_mutex_lock( &p_playlist->object_lock );
01242         o_current_name = [NSString stringWithUTF8String:
01243             p_item->pp_children[i_current]->input.psz_name];
01244         psz_temp = vlc_input_item_GetInfo( &p_item->input ,
01245                    _("Meta-information"),_("Artist") );
01246         o_current_author = [NSString stringWithUTF8String: psz_temp];
01247         free( psz_temp);
01248         vlc_mutex_unlock( &p_playlist->object_lock );
01249 
01250         if( p_selected_item == p_item->pp_children[i_current] &&
01251                     b_selected_item_met == NO )
01252         {
01253             b_selected_item_met = YES;
01254         }
01255         else if( p_selected_item == p_item->pp_children[i_current] &&
01256                     b_selected_item_met == YES )
01257         {
01258             vlc_object_release( p_playlist );
01259             return NULL;
01260         }
01261         else if( b_selected_item_met == YES &&
01262                     ( [o_current_name rangeOfString:[o_search_field
01263                         stringValue] options:NSCaseInsensitiveSearch ].length ||
01264                       [o_current_author rangeOfString:[o_search_field
01265                         stringValue] options:NSCaseInsensitiveSearch ].length ) )
01266         {
01267             vlc_object_release( p_playlist );
01268             /*Adds the parent items in the result array as well, so that we can
01269             expand the tree*/
01270             return [NSMutableArray arrayWithObject: [NSValue
01271                             valueWithPointer: p_item->pp_children[i_current]]];
01272         }
01273         if( p_item->pp_children[i_current]->i_children > 0 )
01274         {
01275             id o_result = [self subSearchItem:
01276                                             p_item->pp_children[i_current]];
01277             if( o_result != NULL )
01278             {
01279                 vlc_object_release( p_playlist );
01280                 [o_result insertObject: [NSValue valueWithPointer:
01281                                 p_item->pp_children[i_current]] atIndex:0];
01282                 return o_result;
01283             }
01284         }
01285     }
01286     vlc_object_release( p_playlist );
01287     return NULL;
01288 }
01289 
01290 - (IBAction)searchItem:(id)sender
01291 {
01292     playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01293                                                        FIND_ANYWHERE );
01294     playlist_view_t * p_view;
01295     id o_result;
01296 
01297     unsigned int i;
01298     int i_row = -1;
01299 
01300     b_selected_item_met = NO;
01301 
01302     if( p_playlist == NULL )
01303         return;
01304     p_view = playlist_ViewFind( p_playlist, i_current_view );
01305 
01306     if( p_view )
01307     {
01308         /*First, only search after the selected item:*
01309          *(b_selected_item_met = NO)                 */
01310         o_result = [self subSearchItem:p_view->p_root];
01311         if( o_result == NULL )
01312         {
01313             /* If the first search failed, search again from the beginning */
01314             o_result = [self subSearchItem:p_view->p_root];
01315         }
01316         if( o_result != NULL )
01317         {
01318             int i_start;
01319             if( [[o_result objectAtIndex: 0] pointerValue] ==
01320                                                     p_playlist->p_general )
01321             i_start = 1;
01322             else
01323             i_start = 0;
01324 
01325             for( i = i_start ; i < [o_result count] - 1 ; i++ )
01326             {
01327                 [o_outline_view expandItem: [o_outline_dict objectForKey:
01328                             [NSString stringWithFormat: @"%p",
01329                             [[o_result objectAtIndex: i] pointerValue]]]];
01330             }
01331             i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
01332                             [NSString stringWithFormat: @"%p",
01333                             [[o_result objectAtIndex: [o_result count] - 1 ]
01334                             pointerValue]]]];
01335         }
01336         if( i_row > -1 )
01337         {
01338             [o_outline_view selectRow:i_row byExtendingSelection: NO];
01339             [o_outline_view scrollRowToVisible: i_row];
01340         }
01341     }
01342     vlc_object_release( p_playlist );
01343 }
01344 
01345 - (IBAction)recursiveExpandNode:(id)sender
01346 {
01347     int i;
01348     id o_item = [o_outline_view itemAtRow: [o_outline_view selectedRow]];
01349     playlist_item_t *p_item = (playlist_item_t *)[o_item pointerValue];
01350 
01351     if( ![[o_outline_view dataSource] outlineView: o_outline_view
01352                                                     isItemExpandable: o_item] )
01353     {
01354         for( i = 0 ; i < p_item->i_parents ; i++ )
01355         {
01356             if( p_item->pp_parents[i]->i_view == i_current_view )
01357             {
01358                 o_item = [o_outline_dict objectForKey: [NSString
01359                     stringWithFormat: @"%p", p_item->pp_parents[i]->p_parent]];
01360                 break;
01361             }
01362         }
01363     }
01364 
01365     /* We need to collapse the node first, since OSX refuses to recursively
01366        expand an already expanded node, even if children nodes are collapsed. */
01367     [o_outline_view collapseItem: o_item collapseChildren: YES];
01368     [o_outline_view expandItem: o_item expandChildren: YES];
01369 }
01370 
01371 - (NSMenu *)menuForEvent:(NSEvent *)o_event
01372 {
01373     NSPoint pt;
01374     vlc_bool_t b_rows;
01375     vlc_bool_t b_item_sel;
01376 
01377     pt = [o_outline_view convertPoint: [o_event locationInWindow]
01378                                                  fromView: nil];
01379     b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
01380                    [o_outline_view selectedRow] != -1 );
01381     b_rows = [o_outline_view numberOfRows] != 0;
01382 
01383     [o_mi_play setEnabled: b_item_sel];
01384     [o_mi_delete setEnabled: b_item_sel];
01385     [o_mi_selectall setEnabled: b_rows];
01386     [o_mi_info setEnabled: b_item_sel];
01387     [o_mi_preparse setEnabled: b_item_sel];
01388     [o_mi_recursive_expand setEnabled: b_item_sel];
01389     [o_mi_sort_name setEnabled: b_item_sel];
01390     [o_mi_sort_author setEnabled: b_item_sel];
01391 
01392     return( o_ctx_menu );
01393 }
01394 
01395 - (void)outlineView: (NSTableView*)o_tv
01396                   didClickTableColumn:(NSTableColumn *)o_tc
01397 {
01398     int i_mode = 0, i_type;
01399     intf_thread_t *p_intf = VLCIntf;
01400     playlist_view_t *p_view;
01401 
01402     playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
01403                                        FIND_ANYWHERE );
01404     if( p_playlist == NULL )
01405     {
01406         return;
01407     }
01408 
01409     /* Check whether the selected table column header corresponds to a
01410        sortable table column*/
01411     if( !( o_tc == o_tc_name || o_tc == o_tc_author ) )
01412     {
01413         vlc_object_release( p_playlist );
01414         return;
01415     }
01416 
01417     p_view = playlist_ViewFind( p_playlist, i_current_view );
01418 
01419     if( o_tc_sortColumn == o_tc )
01420     {
01421         b_isSortDescending = !b_isSortDescending;
01422     }
01423     else
01424     {
01425         b_isSortDescending = VLC_FALSE;
01426     }
01427 
01428     if( o_tc == o_tc_name )
01429     {
01430         i_mode = SORT_TITLE;
01431     }
01432     else if( o_tc == o_tc_author )
01433     {
01434         i_mode = SORT_AUTHOR;
01435     }
01436 
01437     if( b_isSortDescending )
01438     {
01439         i_type = ORDER_REVERSE;
01440     }
01441     else
01442     {
01443         i_type = ORDER_NORMAL;
01444     }
01445 
01446     vlc_mutex_lock( &p_playlist->object_lock );
01447     playlist_RecursiveNodeSort( p_playlist, p_view->p_root, i_mode, i_type );
01448     vlc_mutex_unlock( &p_playlist->object_lock );
01449 
01450     vlc_object_release( p_playlist );
01451     [self playlistUpdated];
01452 
01453     o_tc_sortColumn = o_tc;
01454     [o_outline_view setHighlightedTableColumn:o_tc];
01455 
01456     if( b_isSortDescending )
01457     {
01458         [o_outline_view setIndicatorImage:o_descendingSortingImage
01459                                                         inTableColumn:o_tc];
01460     }
01461     else
01462     {
01463         [o_outline_view setIndicatorImage:o_ascendingSortingImage
01464                                                         inTableColumn:o_tc];
01465     }
01466 }
01467 
01468 
01469 - (void)outlineView:(NSOutlineView *)outlineView
01470                                 willDisplayCell:(id)cell
01471                                 forTableColumn:(NSTableColumn *)tableColumn
01472                                 item:(id)item
01473 {
01474     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01475                                           FIND_ANYWHERE );
01476 
01477     id o_playing_item;
01478 
01479     if( !p_playlist ) return;
01480 
01481     o_playing_item = [o_outline_dict objectForKey:
01482                 [NSString stringWithFormat:@"%p",  p_playlist->status.p_item]];
01483 
01484     if( [self isItem: [o_playing_item pointerValue] inNode:
01485                         [item pointerValue] checkItemExistence: YES]
01486                         || [o_playing_item isEqual: item] )
01487     {
01488         [cell setFont: [NSFont boldSystemFontOfSize: 0]];
01489     }
01490     else
01491     {
01492         [cell setFont: [NSFont systemFontOfSize: 0]];
01493     }
01494     vlc_object_release( p_playlist );
01495 }
01496 
01497 @end
01498 
01499 @implementation VLCPlaylist (NSOutlineViewDataSource)
01500 
01501 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
01502 {
01503     id o_value = [super outlineView: outlineView child: index ofItem: item];
01504     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01505                                                FIND_ANYWHERE );
01506 
01507     if( !p_playlist ) return nil;
01508 
01509     if( p_playlist->i_size >= 2 )
01510     {
01511         [o_status_field setStringValue: [NSString stringWithFormat:
01512                     _NS("%i items in the playlist"), p_playlist->i_size]];
01513     }
01514     else
01515     {
01516         if( p_playlist->i_size == 0 )
01517         {
01518             [o_status_field setStringValue: _NS("No items in the playlist")];
01519         }
01520         else
01521         {
01522             [o_status_field setStringValue: _NS("1 item in the playlist")];
01523         }
01524     }
01525     vlc_object_release( p_playlist );
01526 
01527     [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",
01528                                                     [o_value pointerValue]]];
01529 
01530     return o_value;
01531 
01532 }
01533 
01534 /* Required for drag & drop and reordering */
01535 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
01536 {
01537     unsigned int i;
01538     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01539                                                FIND_ANYWHERE );
01540 
01541     /* First remove the items that were moved during the last drag & drop
01542        operation */
01543     [o_items_array removeAllObjects];
01544     [o_nodes_array removeAllObjects];
01545 
01546     if( !p_playlist ) return NO;
01547 
01548     for( i = 0 ; i < [items count] ; i++ )
01549     {
01550         id o_item = [items objectAtIndex: i];
01551 
01552         /* Refuse to move items that are not in the General Node
01553            (Service Discovery) */
01554         if( ![self isItem: [o_item pointerValue] inNode:
01555                         p_playlist->p_general checkItemExistence: NO])
01556         {
01557             vlc_object_release(p_playlist);
01558             return NO;
01559         }
01560         /* Fill the items and nodes to move in 2 different arrays */
01561         if( ((playlist_item_t *)[o_item pointerValue])->i_children > 0 )
01562             [o_nodes_array addObject: o_item];
01563         else
01564             [o_items_array addObject: o_item];
01565     }
01566 
01567     /* Now we need to check if there are selected items that are in already
01568        selected nodes. In that case, we only want to move the nodes */
01569     [self removeItemsFrom: o_nodes_array ifChildrenOf: o_nodes_array];
01570     [self removeItemsFrom: o_items_array ifChildrenOf: o_nodes_array];
01571 
01572     /* We add the "VLCPlaylistItemPboardType" type to be able to recognize
01573        a Drop operation coming from the playlist. */
01574 
01575     [pboard declareTypes: [NSArray arrayWithObjects:
01576         @"VLCPlaylistItemPboardType", nil] owner: self];
01577     [pboard setData:[NSData data] forType:@"VLCPlaylistItemPboardType"];
01578 
01579     vlc_object_release(p_playlist);
01580     return YES;
01581 }
01582 
01583 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
01584 {
01585     playlist_t *p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01586                                                FIND_ANYWHERE );
01587     NSPasteboard *o_pasteboard = [info draggingPasteboard];
01588 
01589     if( !p_playlist ) return NSDragOperationNone;
01590 
01591     /* Dropping ON items is not allowed if item is not a node */
01592     if( item )
01593     {
01594         if( index == NSOutlineViewDropOnItemIndex &&
01595                 ((playlist_item_t *)[item pointerValue])->i_children == -1 )
01596         {
01597             vlc_object_release( p_playlist );
01598             return NSDragOperationNone;
01599         }
01600     }
01601 
01602     /* We refuse to drop an item in anything else than a child of the General
01603        Node. We still accept items that would be root nodes of the outlineview
01604        however, to allow drop in an empty playlist. */
01605     if( !([self isItem: [item pointerValue] inNode: p_playlist->p_general
01606                                     checkItemExistence: NO] || item == nil) )
01607     {
01608         vlc_object_release( p_playlist );
01609         return NSDragOperationNone;
01610     }
01611 
01612     /* Drop from the Playlist */
01613     if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
01614     {
01615         unsigned int i;
01616         for( i = 0 ; i < [o_nodes_array count] ; i++ )
01617         {
01618             /* We refuse to Drop in a child of an item we are moving */
01619             if( [self isItem: [item pointerValue] inNode:
01620                     [[o_nodes_array objectAtIndex: i] pointerValue]
01621                     checkItemExistence: NO] )
01622             {
01623                 vlc_object_release( p_playlist );
01624                 return NSDragOperationNone;
01625             }
01626         }
01627         vlc_object_release(p_playlist);
01628         return NSDragOperationMove;
01629     }
01630 
01631     /* Drop from the Finder */
01632     else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
01633     {
01634         vlc_object_release(p_playlist);
01635         return NSDragOperationGeneric;
01636     }
01637     vlc_object_release(p_playlist);
01638     return NSDragOperationNone;
01639 }
01640 
01641 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
01642 {
01643     playlist_t * p_playlist =  vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
01644                                                        FIND_ANYWHERE );
01645     NSPasteboard *o_pasteboard = [info draggingPasteboard];
01646 
01647     if( !p_playlist ) return NO;
01648 
01649     /* Drag & Drop inside the playlist */
01650     if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
01651     {
01652         int i_row, i_removed_from_node = 0;
01653         unsigned int i;
01654         playlist_item_t *p_new_parent, *p_item = NULL;
01655         NSArray *o_all_items = [o_nodes_array arrayByAddingObjectsFromArray:
01656                                                                 o_items_array];
01657         /* If the item is to be dropped as root item of the outline, make it a
01658            child of the General node.
01659            Else, choose the proposed parent as parent. */
01660         if( item == nil ) p_new_parent = p_playlist->p_general;
01661         else p_new_parent = [item pointerValue];
01662 
01663         /* Make sure the proposed parent is a node.
01664            (This should never be true) */
01665         if( p_new_parent->i_children < 0 )
01666         {
01667             vlc_object_release( p_playlist );
01668             return NO;
01669         }
01670 
01671         for( i = 0; i < [o_all_items count]; i++ )
01672         {
01673             playlist_item_t *p_old_parent = NULL;
01674             int i_old_index = 0;
01675 
01676             p_item = [[o_all_items objectAtIndex:i] pointerValue];
01677             p_old_parent = [self parentOfItem: p_item];
01678             if( !p_old_parent )
01679             continue;
01680             /* We may need the old index later */
01681             if( p_new_parent == p_old_parent )
01682             {
01683                 int j;
01684                 for( j = 0; j < p_old_parent->i_children; j++ )
01685                 {
01686                     if( p_old_parent->pp_children[j] == p_item )
01687                     {
01688                         i_old_index = j;
01689                         break;
01690                     }
01691                 }
01692             }
01693 
01694             vlc_mutex_lock( &p_playlist->object_lock );
01695             // Acually detach the item from the old position
01696             if( playlist_NodeRemoveItem( p_playlist, p_item, p_old_parent ) ==
01697                 VLC_SUCCESS  &&
01698                 playlist_NodeRemoveParent( p_playlist, p_item, p_old_parent ) ==
01699                 VLC_SUCCESS )
01700             {
01701                 int i_new_index;
01702                 /* Calculate the new index */
01703                 if( index == -1 )
01704                 i_new_index = -1;
01705                 /* If we move the item in the same node, we need to take into
01706                    account that one item will be deleted */
01707                 else
01708                 {
01709                     if ((p_new_parent == p_old_parent &&
01710                                    i_old_index < index + (int)i) )
01711                     {
01712                         i_removed_from_node++;
01713                     }
01714                     i_new_index = index + i - i_removed_from_node;
01715                 }
01716                 // Reattach the item to the new position
01717                 playlist_NodeInsert( p_playlist, i_current_view, p_item,
01718                                                     p_new_parent, i_new_index );
01719             }
01720             vlc_mutex_unlock( &p_playlist->object_lock );
01721         }
01722         [self playlistUpdated];
01723         i_row = [o_outline_view rowForItem:[o_outline_dict
01724             objectForKey:[NSString stringWithFormat: @"%p",
01725             [[o_all_items objectAtIndex: 0] pointerValue]]]];
01726 
01727         if( i_row == -1 )
01728         {
01729             i_row = [o_outline_view rowForItem:[o_outline_dict
01730             objectForKey:[NSString stringWithFormat: @"%p", p_new_parent]]];
01731         }
01732 
01733         [o_outline_view deselectAll: self];
01734         [o_outline_view selectRow: i_row byExtendingSelection: NO];
01735         [o_outline_view scrollRowToVisible: i_row];
01736 
01737         vlc_object_release(p_playlist);
01738         return YES;
01739     }
01740 
01741     else if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
01742     {
01743         int i;
01744         playlist_item_t *p_node = [item pointerValue];
01745 
01746         NSArray *o_array = [NSArray array];
01747         NSArray *o_values = [[o_pasteboard propertyListForType:
01748                                         NSFilenamesPboardType]
01749                                 sortedArrayUsingSelector:
01750                                         @selector(caseInsensitiveCompare:)];
01751 
01752         for( i = 0; i < (int)[o_values count]; i++)
01753         {
01754             NSDictionary *o_dic;
01755             o_dic = [NSDictionary dictionaryWithObject:[o_values
01756                         objectAtIndex:i] forKey:@"ITEM_URL"];
01757             o_array = [o_array arrayByAddingObject: o_dic];
01758         }
01759 
01760         if ( item == nil )
01761         {
01762             [self appendArray: o_array atPos: index enqueue: YES];
01763         }
01764         /* This should never occur */
01765         else if( p_node->i_children == -1 )
01766         {
01767             vlc_object_release( p_playlist );
01768             return NO;
01769         }
01770         else
01771         {
01772             [self appendNodeArray: o_array inNode: p_node
01773                 atPos: index inView: i_current_view enqueue: YES];
01774         }
01775         vlc_object_release( p_playlist );
01776         return YES;
01777     }
01778     vlc_object_release( p_playlist );
01779     return NO;
01780 }
01781 
01782 /* Delegate method of NSWindow */
01783 /*- (void)windowWillClose:(NSNotification *)aNotification
01784 {
01785     [o_btn_playlist setState: NSOffState];
01786 }
01787 */
01788 @end
01789 
01790 

Generated on Tue Dec 20 10:14:38 2005 for vlc-0.8.4a by  doxygen 1.4.2