playlist.m 28.6 KB
Newer Older
1
/*****************************************************************************
2
 * playlist.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2004 VideoLAN
bigben's avatar
...  
bigben committed
5
 * $Id$
6 7
 *
 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8
 *          Derk-Jan Hartman <hartman at videolan dot org>
9
 *          Benjamin Pracht <bigben at videolab dot org>
10 11 12 13 14
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
bigben's avatar
bigben committed
15
 *
16 17 18 19 20 21 22 23 24 25
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

26
/* TODO
27
 * add 'icons' for different types of nodes? (http://www.cocoadev.com/index.pl?IconAndTextInTableCell)
28 29 30 31 32 33 34 35 36 37 38
 * create a new 'playlist toggle' that hides the playlist and in effect give you the old controller
 * create a new search field build with pictures from the 'regular' search field, so it can be emulated on 10.2
 * create toggle buttons for the shuffle, repeat one, repeat all functions.
 * implement drag and drop and item reordering.
 * reimplement enable/disable item
 * create a new 'tool' button (see the gear button in the Finder window) for 'actions'
   (adding service discovery, other views, new node/playlist, save node/playlist) stuff like that
 */



39 40 41 42 43 44
/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <sys/param.h>                                    /* for MAXPATHLEN */
#include <string.h>
45
#include <math.h>
hartman's avatar
hartman committed
46
#include <sys/mount.h>
bigben's avatar
bigben committed
47
#include <vlc_keys.h>
48 49 50

#include "intf.h"
#include "playlist.h"
hartman's avatar
hartman committed
51
#include "controls.h"
52
#include "osd.h"
53 54 55 56 57 58 59 60

/*****************************************************************************
 * VLCPlaylistView implementation 
 *****************************************************************************/
@implementation VLCPlaylistView

- (NSMenu *)menuForEvent:(NSEvent *)o_event
{
Jon Lech Johansen's avatar
Jon Lech Johansen committed
61
    return( [[self delegate] menuForEvent: o_event] );
62 63
}

64 65 66
- (void)keyDown:(NSEvent *)o_event
{
    unichar key = 0;
bigben's avatar
*all :  
bigben committed
67

68 69 70 71 72 73 74
    if( [[o_event characters] length] )
    {
        key = [[o_event characters] characterAtIndex: 0];
    }

    switch( key )
    {
75 76 77 78
        case NSDeleteCharacter:
        case NSDeleteFunctionKey:
        case NSDeleteCharFunctionKey:
        case NSBackspaceCharacter:
bigben's avatar
bigben committed
79
            [[self delegate] deleteItem:self];
80
            break;
bigben's avatar
*all :  
bigben committed
81

82 83 84 85 86 87
        default:
            [super keyDown: o_event];
            break;
    }
}

88 89 90 91 92 93 94
@end

/*****************************************************************************
 * VLCPlaylist implementation 
 *****************************************************************************/
@implementation VLCPlaylist

hartman's avatar
hartman committed
95 96 97 98 99
- (id)init
{
    self = [super init];
    if ( self !=nil )
    {
100
        //i_moveRow = -1;
hartman's avatar
hartman committed
101 102 103 104
    }
    return self;
}

105 106
- (void)awakeFromNib
{
hartman's avatar
hartman committed
107 108 109
    [o_outline_view setTarget: self];
    [o_outline_view setDelegate: self];
    [o_outline_view setDataSource: self];
110

111
    [o_outline_view setDoubleAction: @selector(playItem:)];
112

hartman's avatar
hartman committed
113
    [o_outline_view registerForDraggedTypes: 
114
        [NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
hartman's avatar
hartman committed
115
    [o_outline_view setIntercellSpacing: NSMakeSize (0.0, 1.0)];
116

bigben's avatar
bigben committed
117 118 119
/* We need to check whether _defaultTableHeaderSortImage exists, since it 
belongs to an Apple hidden private API, and then can "disapear" at any time*/

120
    if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderSortImage)] )
bigben's avatar
bigben committed
121
    {
122
        o_ascendingSortingImage = [[NSOutlineView class] _defaultTableHeaderSortImage];
bigben's avatar
bigben committed
123 124 125
    }
    else
    {
hartman's avatar
hartman committed
126
        o_ascendingSortingImage = nil;
bigben's avatar
bigben committed
127 128
    }

129
    if( [[NSOutlineView class] respondsToSelector:@selector(_defaultTableHeaderReverseSortImage)] )
bigben's avatar
bigben committed
130
    {
131
        o_descendingSortingImage = [[NSOutlineView class] _defaultTableHeaderReverseSortImage];
bigben's avatar
bigben committed
132 133 134 135 136 137
    }
    else
    {
        o_descendingSortingImage = nil;
    }

bigben's avatar
bigben committed
138
    o_outline_dict = [[NSMutableDictionary alloc] init];
139
    o_tc_sortColumn = nil;
bigben's avatar
bigben committed
140

141
    [self initStrings];
142
    //[self playlistUpdated];
143
}
hartman's avatar
hartman committed
144

145 146
- (void)initStrings
{
147
    [o_mi_save_playlist setTitle: _NS("Save Playlist...")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
148 149 150
    [o_mi_play setTitle: _NS("Play")];
    [o_mi_delete setTitle: _NS("Delete")];
    [o_mi_selectall setTitle: _NS("Select All")];
151
    [o_mi_info setTitle: _NS("Properties")];
152 153
    [o_mi_sort_name setTitle: _NS("Sort Node by Name")];
    [o_mi_sort_author setTitle: _NS("Sort Node by Author")];
154 155
    [[o_tc_name headerCell] setStringValue:_NS("Name")];
    [[o_tc_author headerCell] setStringValue:_NS("Author")];
156
    [[o_tc_duration headerCell] setStringValue:_NS("Duration")];
bigben's avatar
bigben committed
157 158 159
    [o_status_field setStringValue: [NSString stringWithFormat:
                        _NS("0 items in playlist")]];

160
    [o_random_ckb setTitle: _NS("Random")];
161
#if 0
162
    [o_search_button setTitle: _NS("Search")];
163
#endif
164
    [o_btn_playlist setToolTip: _NS("Playlist")];
165 166 167
    [[o_loop_popup itemAtIndex:0] setTitle: _NS("Standard Play")];
    [[o_loop_popup itemAtIndex:1] setTitle: _NS("Repeat One")];
    [[o_loop_popup itemAtIndex:2] setTitle: _NS("Repeat All")];
168 169
}

170 171
- (void)playlistUpdated
{
172 173 174 175 176 177 178 179 180 181 182
    unsigned int i;

    /* Clear indications of any existing column sorting*/
    for( i = 0 ; i < [[o_outline_view tableColumns] count] ; i++ )
    {
        [o_outline_view setIndicatorImage:nil inTableColumn:
                            [[o_outline_view tableColumns] objectAtIndex:i]];
    }

    [o_outline_view setHighlightedTableColumn:nil];
    o_tc_sortColumn = nil;
bigben's avatar
bigben committed
183
    [o_outline_dict removeAllObjects];
184 185
    [o_outline_view reloadData];
}
186

bigben's avatar
bigben committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
- (bool)isItem:(playlist_item_t *)p_item inNode:(playlist_item_t *)p_node
{
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
                                          FIND_ANYWHERE );
    playlist_item_t * p_temp_item = p_item;

    if ( p_playlist == NULL )
    {
        return NO;
    }

    while ( p_temp_item->i_parents > 0 )
    {
        int i;
        for (i = 0; i < p_temp_item->i_parents ; i++)
        {
            if (p_temp_item->pp_parents[i]->i_view == VIEW_SIMPLE)
            {
                if (p_temp_item->pp_parents[i]->p_parent == p_node)
                {
                    vlc_object_release(p_playlist);
                    return YES;
                }
                else
                {
                    p_temp_item = p_temp_item->pp_parents[i]->p_parent;
                    break;
                }
            }
        }
    }

    vlc_object_release(p_playlist);
    return NO;
}


224 225 226 227 228 229 230 231 232
- (IBAction)playItem:(id)sender
{
    intf_thread_t * p_intf = VLCIntf;
    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                       FIND_ANYWHERE );

    if( p_playlist != NULL )
    {
        playlist_item_t *p_item;
bigben's avatar
 
bigben committed
233 234 235
        playlist_item_t *p_node = NULL;
        int i;

236
        p_item = [[o_outline_view itemAtRow:[o_outline_view selectedRow]] pointerValue];
bigben's avatar
 
bigben committed
237

238
        if( p_item )
bigben's avatar
 
bigben committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
        {
            if (p_item->i_children == -1)
            {
                for (i = 0 ; i < p_item->i_parents ; i++)
                {
                    if (p_item->pp_parents[i]->i_view == VIEW_SIMPLE)
                    {
                        p_node = p_item->pp_parents[i]->p_parent;
                    }
                }
            }
            else
            {
                p_node = p_item;
                if (p_node->pp_children[0]->i_children == -1 &&
                    p_node->i_children > 0)
                {
                    p_item = p_node->pp_children[0];
                }
                else
                {
                    p_item = NULL;
                }
            }

//        p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );


            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VIEW_SIMPLE, p_node, p_item );
//            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VIEW_SIMPLE, p_view ? p_view->p_root : NULL, p_item );
        }
270 271 272
        vlc_object_release( p_playlist );
    }
}
273

bigben's avatar
bigben committed
274 275 276 277 278
- (IBAction)selectAll:(id)sender
{
    [o_outline_view selectAll: nil];
}

bigben's avatar
bigben committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
- (IBAction)deleteItem:(id)sender
{
    int i, c, i_row;
    NSMutableArray *o_to_delete;
    NSNumber *o_number;

    playlist_t * p_playlist;
    intf_thread_t * p_intf = VLCIntf;

    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                          FIND_ANYWHERE );

    if ( p_playlist == NULL )
    {
        return;
    }
    o_to_delete = [NSMutableArray arrayWithArray:[[o_outline_view selectedRowEnumerator] allObjects]];
    c = [o_to_delete count];

    for( i = 0; i < c; i++ ) {
        playlist_item_t * p_item;
        o_number = [o_to_delete lastObject];
        i_row = [o_number intValue];

        [o_to_delete removeObject: o_number];
        [o_outline_view deselectRow: i_row];
        p_item = (playlist_item_t *)[[o_outline_view itemAtRow: i_row]pointerValue];
        if (p_item->i_children > -1)
        {
            if (p_playlist->status.i_status)
            {
                if ([self isItem:p_playlist->status.p_item inNode: p_item]
                                    == YES && p_playlist->status.i_status)
                {
                    playlist_Stop( p_playlist );
                }
            }
            playlist_NodeDelete( p_playlist, p_item, VLC_TRUE);
        }
        else
        {
            if( p_playlist->status.p_item == [[o_outline_view itemAtRow: i_row]
                        pointerValue] && p_playlist->status.i_status )
            {
                playlist_Stop( p_playlist );
            }
325
            playlist_LockDelete( p_playlist, p_item->input.i_id );
bigben's avatar
bigben committed
326 327 328 329
        }
        [self playlistUpdated];
    }
}
bigben's avatar
bigben committed
330

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
- (IBAction)sortNodeByName:(id)sender
{
    [self sortNode: SORT_TITLE];
}

- (IBAction)sortNodeByAuthor:(id)sender
{
    [self sortNode: SORT_AUTHOR];
}

- (void)sortNode:(int)i_mode
{
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
                                          FIND_ANYWHERE );
    playlist_item_t * p_item;

    if (p_playlist == NULL)
    {
        return;
    }

    if ([o_outline_view selectedRow] > -1)
    {
        p_item = [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
                                                                pointerValue];
    }
    else
    /*If no item is selected, sort the whole playlist*/
    {
        playlist_view_t * p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
        p_item = p_view->p_root;
    }

    if (p_item->i_children > -1)
    {
        vlc_mutex_lock(&p_playlist->object_lock );
        playlist_RecursiveNodeSort( p_playlist, p_item, i_mode, ORDER_NORMAL );
        vlc_mutex_unlock(&p_playlist->object_lock );
    }
    else
    {
        int i;

        for (i = 0 ; i < p_item->i_parents ; i++)
        {
            if (p_item->pp_parents[i]->i_view == VIEW_SIMPLE)
            {
                vlc_mutex_lock(&p_playlist->object_lock );
                playlist_RecursiveNodeSort( p_playlist,
                        p_item->pp_parents[i]->p_parent, i_mode, ORDER_NORMAL );
                vlc_mutex_unlock(&p_playlist->object_lock );
                break;
            }
        }
    }
    vlc_object_release(p_playlist);
    [self playlistUpdated];
}

390
- (void)appendArray:(NSArray*)o_array atPos:(int)i_position enqueue:(BOOL)b_enqueue
391
{
392
    int i_item;
hartman's avatar
hartman committed
393
    intf_thread_t * p_intf = VLCIntf;
394 395 396 397 398 399 400 401
    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                       FIND_ANYWHERE );

    if( p_playlist == NULL )
    {
        return;
    }

402
    for ( i_item = 0; i_item < (int)[o_array count]; i_item++ )
403
    {
404 405
        /* One item */
        NSDictionary *o_one_item;
hartman's avatar
hartman committed
406
        int j, i_total_options = 0, i_new_id = -1;
hartman's avatar
hartman committed
407 408
        int i_mode = PLAYLIST_INSERT;
        BOOL b_rem = FALSE, b_dir = FALSE;
409
        NSString *o_uri, *o_name;
410
        NSArray *o_options;
411
        NSURL *o_true_file;
hartman's avatar
hartman committed
412
        char **ppsz_options = NULL;
bigben's avatar
*all :  
bigben committed
413

414 415
        /* Get the item */
        o_one_item = [o_array objectAtIndex: i_item];
416
        o_uri = (NSString *)[o_one_item objectForKey: @"ITEM_URL"];
417 418
        o_name = (NSString *)[o_one_item objectForKey: @"ITEM_NAME"];
        o_options = (NSArray *)[o_one_item objectForKey: @"ITEM_OPTIONS"];
bigben's avatar
*all :  
bigben committed
419

hartman's avatar
hartman committed
420
        /* If no name, then make a guess */
421
        if( !o_name) o_name = [[NSFileManager defaultManager] displayNameAtPath: o_uri];
bigben's avatar
*all :  
bigben committed
422

423 424
        if( [[NSFileManager defaultManager] fileExistsAtPath:o_uri isDirectory:&b_dir] && b_dir &&
            [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath: o_uri isRemovable: &b_rem
hartman's avatar
hartman committed
425 426 427 428 429
                    isWritable:NULL isUnmountable:NULL description:NULL type:NULL] && b_rem   )
        {
            /* All of this is to make sure CD's play when you D&D them on VLC */
            /* Converts mountpoint to a /dev file */
            struct statfs *buf;
hartman's avatar
hartman committed
430
            char *psz_dev;
hartman's avatar
hartman committed
431
            buf = (struct statfs *) malloc (sizeof(struct statfs));
432
            statfs( [o_uri fileSystemRepresentation], buf );
hartman's avatar
hartman committed
433
            psz_dev = strdup(buf->f_mntfromname);
434
            o_uri = [NSString stringWithCString: psz_dev ];
hartman's avatar
hartman committed
435
        }
hartman's avatar
hartman committed
436 437

        if( o_options && [o_options count] > 0 )
438
        {
hartman's avatar
hartman committed
439 440
            /* Count the input options */
            i_total_options = [o_options count];
bigben's avatar
*all :  
bigben committed
441

hartman's avatar
hartman committed
442 443
            /* Allocate ppsz_options */
            for( j = 0; j < i_total_options; j++ )
444
            {
hartman's avatar
hartman committed
445 446
                if( !ppsz_options )
                    ppsz_options = (char **)malloc( sizeof(char *) * i_total_options );
bigben's avatar
*all :  
bigben committed
447

hartman's avatar
hartman committed
448
                ppsz_options[j] = strdup([[o_options objectAtIndex:j] UTF8String]);
449 450
            }
        }
hartman's avatar
hartman committed
451 452

        /* Add the item */
bigben's avatar
*all :  
bigben committed
453 454
        i_new_id = playlist_AddExt( p_playlist, [o_uri fileSystemRepresentation],
                      [o_name UTF8String], i_mode,
hartman's avatar
hartman committed
455 456 457
                      i_position == -1 ? PLAYLIST_END : i_position + i_item,
                      0, (ppsz_options != NULL ) ? (const char **)ppsz_options : 0, i_total_options );

bigben's avatar
*all :  
bigben committed
458
        /* clean up
hartman's avatar
hartman committed
459 460 461 462
        for( j = 0; j < i_total_options; j++ )
            free( ppsz_options[j] );
        if( ppsz_options ) free( ppsz_options ); */

hartman's avatar
hartman committed
463
        /* Recent documents menu */
464 465
        o_true_file = [NSURL fileURLWithPath: o_uri];
        if( o_true_file != nil )
bigben's avatar
*all :  
bigben committed
466
        {
467
            [[NSDocumentController sharedDocumentController]
bigben's avatar
*all :  
bigben committed
468
                noteNewRecentDocumentURL: o_true_file];
469
        }
bigben's avatar
*all :  
bigben committed
470

hartman's avatar
hartman committed
471 472 473 474 475
        if( i_item == 0 && !b_enqueue )
        {
            playlist_Goto( p_playlist, playlist_GetPositionById( p_playlist, i_new_id ) );
            playlist_Play( p_playlist );
        }
476 477 478
    }

    vlc_object_release( p_playlist );
Jon Lech Johansen's avatar
Jon Lech Johansen committed
479 480
}

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
- (IBAction)handlePopUp:(id)sender

{
             intf_thread_t * p_intf = VLCIntf;
             vlc_value_t val1,val2;
             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                        FIND_ANYWHERE );
             if( p_playlist == NULL )
             {
                 return;
             }

    switch ([o_loop_popup indexOfSelectedItem])
    {
        case 1:

             val1.b_bool = 0;
             var_Set( p_playlist, "loop", val1 );
             val1.b_bool = 1;
             var_Set( p_playlist, "repeat", val1 );
             vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat One" ) );
        break;

        case 2:
             val1.b_bool = 0;
             var_Set( p_playlist, "repeat", val1 );
             val1.b_bool = 1;
             var_Set( p_playlist, "loop", val1 );
             vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat All" ) );
        break;

        default:
             var_Get( p_playlist, "repeat", &val1 );
             var_Get( p_playlist, "loop", &val2 );
             if (val1.b_bool || val2.b_bool)
             {
                  val1.b_bool = 0;
                  var_Set( p_playlist, "repeat", val1 );
                  var_Set( p_playlist, "loop", val1 );
                  vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Repeat Off" ) );
             }
         break;
     }
     vlc_object_release( p_playlist );
     [self playlistUpdated];
}

bigben's avatar
bigben committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
- (NSMutableArray *)subSearchItem:(playlist_item_t *)p_item
{
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
                                                       FIND_ANYWHERE );
    playlist_item_t * p_selected_item;
    int i_current, i_selected_row;

    if (!p_playlist)
        return NULL;

    i_selected_row = [o_outline_view selectedRow];
    if (i_selected_row < 0)
        i_selected_row = 0;

    p_selected_item = (playlist_item_t *)[[o_outline_view itemAtRow:
                                            i_selected_row] pointerValue];

    for (i_current = 0; i_current < p_item->i_children ; i_current++)
    {
        char * psz_temp;
        NSString * o_current_name, * o_current_author;

        vlc_mutex_lock( &p_playlist->object_lock );
        o_current_name = [NSString stringWithUTF8String:
            p_item->pp_children[i_current]->input.psz_name];
        psz_temp = playlist_ItemGetInfo(p_item ,_("Meta-information"),_("Author") );
        o_current_author = [NSString stringWithUTF8String: psz_temp];
        free( psz_temp);
        vlc_mutex_unlock( &p_playlist->object_lock );

        if (p_selected_item == p_item->pp_children[i_current] &&
                    b_selected_item_met == NO)
        {
            b_selected_item_met = YES;
        }
        else if (p_selected_item == p_item->pp_children[i_current] &&
                    b_selected_item_met == YES)
        {
            vlc_object_release(p_playlist);
            return NULL;
        }
        else if (b_selected_item_met == YES &&
                    ([o_current_name rangeOfString:[o_search_field
                        stringValue] options:NSCaseInsensitiveSearch ].length ||
                    [o_current_author rangeOfString:[o_search_field
                        stringValue] options:NSCaseInsensitiveSearch ].length))
        {
            vlc_object_release(p_playlist);
            /*Adds the parent items in the result array as well, so that we can
            expand the tree*/
            return [NSMutableArray arrayWithObject: [NSValue
                            valueWithPointer: p_item->pp_children[i_current]]];
        }
        if (p_item->pp_children[i_current]->i_children > 0)
        {
            id o_result = [self subSearchItem:
                                            p_item->pp_children[i_current]];
            if (o_result != NULL)
            {
                vlc_object_release(p_playlist);
                [o_result insertObject: [NSValue valueWithPointer:
                                p_item->pp_children[i_current]] atIndex:0];
                return o_result;
            }
        }
    }
    vlc_object_release(p_playlist);
    return NULL;
}

- (IBAction)searchItem:(id)sender
{
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
                                                       FIND_ANYWHERE );
    playlist_view_t * p_view;
    id o_result;

    unsigned int i;
    int i_row = -1;

    b_selected_item_met = NO;

    if( p_playlist == NULL )
        return;

    p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );

    if (p_view)
    {
        /*First, only search after the selected item:*
         *(b_selected_item_met = NO)                 */
        o_result = [self subSearchItem:p_view->p_root];
        if (o_result == NULL)
        {
            /* If the first search failed, search again from the beginning */
            o_result = [self subSearchItem:p_view->p_root];
        }
        if (o_result != NULL)
        {
            for (i = 0 ; i < [o_result count] - 1 ; i++)
            {
                [o_outline_view expandItem: [o_outline_dict objectForKey:
                            [NSString stringWithFormat: @"%p",
                            [[o_result objectAtIndex: i] pointerValue]]]];
            }
            i_row = [o_outline_view rowForItem: [o_outline_dict objectForKey:
                            [NSString stringWithFormat: @"%p",
                            [[o_result objectAtIndex: [o_result count] - 1 ]
                            pointerValue]]]];
        }
        if (i_row > -1)
        {
            [o_outline_view selectRow:i_row byExtendingSelection: NO];
            [o_outline_view scrollRowToVisible: i_row];
        }
    }
    vlc_object_release(p_playlist);

}

bigben's avatar
bigben committed
648 649 650 651 652 653
- (NSMenu *)menuForEvent:(NSEvent *)o_event
{
    NSPoint pt;
    vlc_bool_t b_rows;
    vlc_bool_t b_item_sel;

654
    pt = [o_outline_view convertPoint: [o_event locationInWindow]
bigben's avatar
bigben committed
655
                                                 fromView: nil];
656 657 658
    b_item_sel = ( [o_outline_view rowAtPoint: pt] != -1 &&
                   [o_outline_view selectedRow] != -1 );
    b_rows = [o_outline_view numberOfRows] != 0;
bigben's avatar
bigben committed
659 660 661 662 663 664 665 666

    [o_mi_play setEnabled: b_item_sel];
    [o_mi_delete setEnabled: b_item_sel];
    [o_mi_selectall setEnabled: b_rows];
    [o_mi_info setEnabled: b_item_sel];

    return( o_ctx_menu );
}
667

668 669 670 671 672 673
- (playlist_item_t *)selectedPlaylistItem
{
    return [[o_outline_view itemAtRow: [o_outline_view selectedRow]]
                                                                pointerValue];
}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
- (void) outlineView:(NSTableView*)o_tv
                  didClickTableColumn:(NSTableColumn *)o_tc
{
    intf_thread_t * p_intf = VLCIntf;
    playlist_t *p_playlist =
        (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                       FIND_ANYWHERE );
    playlist_view_t * p_view  = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
    int i_mode = 0, i_type;

    if( p_playlist == NULL )
    {
        return;
    }

    /* Check whether the selected table column header corresponds to a
       sortable table column*/
    if ( !(o_tc == o_tc_name || o_tc == o_tc_author))
    {
        return;
    }

    if( o_tc_sortColumn == o_tc )
    {
        b_isSortDescending = !b_isSortDescending;
    }
    else
    {
        b_isSortDescending = VLC_FALSE;
    }

    if (o_tc == o_tc_name)
    {
        i_mode = SORT_TITLE;
    }
    else if (o_tc == o_tc_author)
    {
        i_mode = SORT_AUTHOR;
    }

    if (b_isSortDescending)
    {
        i_type = ORDER_REVERSE;
    }
    else
    {
        i_type = ORDER_NORMAL;
    }

    vlc_mutex_lock(&p_playlist->object_lock );
    playlist_RecursiveNodeSort(p_playlist, p_view->p_root, i_mode, i_type);
    vlc_mutex_unlock(&p_playlist->object_lock );

    vlc_object_release( p_playlist );
    [self playlistUpdated];

    o_tc_sortColumn = o_tc;
    [o_outline_view setHighlightedTableColumn:o_tc];

    if (b_isSortDescending)
    {
        [o_outline_view setIndicatorImage:o_descendingSortingImage
                                                        inTableColumn:o_tc];
    }
    else
    {
        [o_outline_view setIndicatorImage:o_ascendingSortingImage
                                                        inTableColumn:o_tc];
    }
}

hartman's avatar
hartman committed
745
@end
bigben's avatar
bigben committed
746

hartman's avatar
hartman committed
747
@implementation VLCPlaylist (NSOutlineViewDataSource)
748

hartman's avatar
hartman committed
749 750
/* return the number of children for Obj-C pointer item */ /* DONE */
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
hartman's avatar
ALL:  
hartman committed
751
{
hartman's avatar
hartman committed
752 753
    int i_return = 0;
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
hartman's avatar
ALL:  
hartman committed
754 755
                                                       FIND_ANYWHERE );
    if( p_playlist == NULL )
hartman's avatar
hartman committed
756 757 758
        return 0;

    if( item == nil )
hartman's avatar
ALL:  
hartman committed
759
    {
hartman's avatar
hartman committed
760 761
        /* root object */
        playlist_view_t *p_view;
762
        p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
hartman's avatar
hartman committed
763 764 765 766 767 768 769 770
        if( p_view && p_view->p_root )
            i_return = p_view->p_root->i_children;
    }
    else
    {
        playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
        if( p_item )
            i_return = p_item->i_children;
hartman's avatar
ALL:  
hartman committed
771 772
    }
    vlc_object_release( p_playlist );
hartman's avatar
hartman committed
773
    if( i_return == -1 ) i_return = 0;
774
    msg_Dbg( p_playlist, "I have %d children", i_return );
hartman's avatar
hartman committed
775
    return i_return;
hartman's avatar
ALL:  
hartman committed
776
}
bigben's avatar
bigben committed
777

hartman's avatar
hartman committed
778 779
/* return the child at index for the Obj-C pointer item */ /* DONE */
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
bigben's avatar
...  
bigben committed
780
{
781
    playlist_item_t *p_return = NULL;
hartman's avatar
hartman committed
782
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
bigben's avatar
*all :  
bigben committed
783
                                                       FIND_ANYWHERE );
bigben's avatar
bigben committed
784 785
    NSValue * o_value;

hartman's avatar
hartman committed
786
    if( p_playlist == NULL )
787
        return nil;
bigben's avatar
*all :  
bigben committed
788

hartman's avatar
hartman committed
789
    if( item == nil )
bigben's avatar
*all :  
bigben committed
790
    {
hartman's avatar
hartman committed
791 792
        /* root object */
        playlist_view_t *p_view;
793
        p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
hartman's avatar
hartman committed
794 795 796 797 798 799 800
        if( p_view && index < p_view->p_root->i_children )
            p_return = p_view->p_root->pp_children[index];
    }
    else
    {
        playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
        if( p_item && index < p_item->i_children )
bigben's avatar
*all :  
bigben committed
801
        {
hartman's avatar
hartman committed
802
            p_return = p_item->pp_children[index];
bigben's avatar
*all :  
bigben committed
803 804 805
        }
    }

bigben's avatar
bigben committed
806 807 808
    [o_status_field setStringValue: [NSString stringWithFormat:
                        _NS("%i items in playlist"), p_playlist->i_size]];

hartman's avatar
hartman committed
809
    vlc_object_release( p_playlist );
810
    msg_Dbg( p_playlist, "childitem with index %d", index );
bigben's avatar
bigben committed
811 812 813 814 815

    o_value = [NSValue valueWithPointer: p_return];

    [o_outline_dict setObject:o_value forKey:[NSString stringWithFormat:@"%p",                                                                      p_return]];
    return o_value;
816 817
}

hartman's avatar
hartman committed
818 819
/* is the item expandable */
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
820
{
hartman's avatar
hartman committed
821 822
    int i_return = 0;
    playlist_t * p_playlist = vlc_object_find( VLCIntf, VLC_OBJECT_PLAYLIST,
823
                                                       FIND_ANYWHERE );
hartman's avatar
hartman committed
824
    if( p_playlist == NULL )
825
        return NO;
826

hartman's avatar
hartman committed
827
    if( item == nil )
828
    {
hartman's avatar
hartman committed
829 830
        /* root object */
        playlist_view_t *p_view;
831
        p_view = playlist_ViewFind( p_playlist, VIEW_SIMPLE );
hartman's avatar
hartman committed
832 833
        if( p_view && p_view->p_root )
            i_return = p_view->p_root->i_children;
834
    }
hartman's avatar
hartman committed
835 836 837 838 839 840 841 842
    else
    {
        playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
        if( p_item )
            i_return = p_item->i_children;
    }
    vlc_object_release( p_playlist );

843
    if( i_return == -1 || i_return == 0 )
hartman's avatar
hartman committed
844 845 846
        return NO;
    else
        return YES;
847 848
}

hartman's avatar
hartman committed
849
/* retrieve the string values for the cells */
850
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)o_tc byItem:(id)item
851 852
{
    id o_value = nil;
hartman's avatar
hartman committed
853
    intf_thread_t * p_intf = VLCIntf;
854 855
    playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                               FIND_ANYWHERE );
856
    playlist_item_t *p_item = (playlist_item_t *)[item pointerValue];
857

hartman's avatar
hartman committed
858
    if( p_playlist == NULL || p_item == NULL )
859
    {
860
        return( @"error" );
861 862
    }

hartman's avatar
hartman committed
863
    if( [[o_tc identifier] isEqualToString:@"1"] )
864
    {
bigben's avatar
*all :  
bigben committed
865
        o_value = [NSString stringWithUTF8String:
hartman's avatar
hartman committed
866
            p_item->input.psz_name];
867
        if( o_value == NULL )
bigben's avatar
*all :  
bigben committed
868
            o_value = [NSString stringWithCString:
hartman's avatar
hartman committed
869
                p_item->input.psz_name];
870 871 872
    }
    else if( [[o_tc identifier] isEqualToString:@"2"] )
    {
hartman's avatar
hartman committed
873
        char *psz_temp;
874
        psz_temp = playlist_ItemGetInfo( p_item ,_("Meta-information"),_("Artist") );
hartman's avatar
hartman committed
875 876 877 878 879 880 881 882 883 884 885 886

        if( psz_temp == NULL )
            o_value = @"";
        else
        {
            o_value = [NSString stringWithUTF8String: psz_temp];
            if( o_value == NULL )
            {
                o_value = [NSString stringWithCString: psz_temp];
            }
            free( psz_temp );
        }
887
    }
888 889 890
    else if( [[o_tc identifier] isEqualToString:@"3"] )
    {
        char psz_duration[MSTRTIME_MAX_SIZE];
hartman's avatar
hartman committed
891
        mtime_t dur = p_item->input.i_duration;
892 893 894 895 896 897 898 899 900 901
        if( dur != -1 )
        {
            secstotimestr( psz_duration, dur/1000000 );
            o_value = [NSString stringWithUTF8String: psz_duration];
        }
        else
        {
            o_value = @"-:--:--";
        }
    }
902 903 904 905 906 907

    vlc_object_release( p_playlist );

    return( o_value );
}

hartman's avatar
hartman committed
908 909
/* Required for drag & drop and reordering */
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
hartman's avatar
hartman committed
910 911 912 913
{
    return NO;
}

hartman's avatar
hartman committed
914
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
hartman's avatar
hartman committed
915 916 917 918
{
    return NSDragOperationNone;
}

919 920 921 922 923 924
/* Delegate method of NSWindow */
- (void)windowWillClose:(NSNotification *)aNotification
{
    [o_btn_playlist setState: NSOffState];
}

925 926
@end

927