playlistinfo.m 18.3 KB
Newer Older
Benjamin Pracht's avatar
Benjamin Pracht committed
1
/*****************************************************************************
2
 r playlistinfo.m: MacOS X interface module
Benjamin Pracht's avatar
Benjamin Pracht committed
3
 *****************************************************************************
4
 * Copyright (C) 2002-2008 the VideoLAN team
5
 * $Id$
Benjamin Pracht's avatar
Benjamin Pracht committed
6
 *
7
 * Authors: Benjamin Pracht <bigben at videolan dot org>
8
 *          Felix Paul Kühne <fkuehne at videolan dot org>
Benjamin Pracht's avatar
Benjamin Pracht committed
9 10 11 12 13
 *
 * 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.
14
 *
Benjamin Pracht's avatar
Benjamin Pracht committed
15 16 17 18 19 20 21
 * 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
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Benjamin Pracht's avatar
Benjamin Pracht committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include "intf.h"
#include "playlistinfo.h"
#include "playlist.h"

/*****************************************************************************
 * VLCPlaylistInfo Implementation
 *****************************************************************************/

37 38
@implementation VLCInfo

39 40 41
static VLCInfo *_o_sharedInstance = nil;

+ (VLCInfo *)sharedInstance
42
{
43 44
    return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
}
45

46 47 48 49 50 51 52 53 54 55 56
- (id)init
{
    if( _o_sharedInstance ) {
        [self dealloc];
    } else {
        _o_sharedInstance = [super init];
        
        if( _o_sharedInstance != nil )
        {
            p_item = NULL;
            o_statUpdateTimer = nil;
57 58
            [self updatePanelWithItem: NULL];
            rootItem = [[VLCInfoTreeItem alloc] init];
59
        }
60
    }
61 62

    return _o_sharedInstance;
63 64
}

Benjamin Pracht's avatar
Benjamin Pracht committed
65 66
- (void)awakeFromNib
{
67 68
    [o_info_window setExcludedFromWindowsMenu: YES];
    [o_info_window setFloatingPanel: NO];
Benjamin Pracht's avatar
Benjamin Pracht committed
69

70 71
    [o_info_window setTitle: _NS("Media Information")];
    [o_uri_lbl setStringValue: _NS("Location")];
Benjamin Pracht's avatar
Benjamin Pracht committed
72
    [o_title_lbl setStringValue: _NS("Title")];
73 74 75
    [o_author_lbl setStringValue: _NS("Artist")];
    [o_saveMetaData_btn setStringValue: _NS("Save Metadata" )];

76
    [[o_tab_view tabViewItemAtIndex: 0] setLabel: _NS("General")];
77
    [[o_tab_view tabViewItemAtIndex: 1] setLabel: _NS("Codec Details")];
78 79 80 81 82 83
    [[o_tab_view tabViewItemAtIndex: 2] setLabel: _NS("Statistics")];
    [o_tab_view selectTabViewItemAtIndex: 0];

    /* constants defined in vlc_meta.h */
    [o_genre_lbl setStringValue: _NS(VLC_META_GENRE)];
    [o_copyright_lbl setStringValue: _NS(VLC_META_COPYRIGHT)];
84 85
    [o_collection_lbl setStringValue: _NS(VLC_META_ALBUM)];
    [o_seqNum_lbl setStringValue: _NS(VLC_META_TRACK_NUMBER)];
86 87 88 89 90
    [o_description_lbl setStringValue: _NS(VLC_META_DESCRIPTION)];
    [o_date_lbl setStringValue: _NS(VLC_META_DATE)];
    [o_language_lbl setStringValue: _NS(VLC_META_LANGUAGE)];
    [o_nowPlaying_lbl setStringValue: _NS(VLC_META_NOW_PLAYING)];
    [o_publisher_lbl setStringValue: _NS(VLC_META_PUBLISHER)];
91

92 93 94 95 96 97
    /* statistics */
    [o_input_box setTitle: _NS("Input")];
    [o_read_bytes_lbl setStringValue: _NS("Read at media")];
    [o_input_bitrate_lbl setStringValue: _NS("Input bitrate")];
    [o_demux_bytes_lbl setStringValue: _NS("Demuxed")];
    [o_demux_bitrate_lbl setStringValue: _NS("Stream bitrate")];
98

99 100 101 102
    [o_video_box setTitle: _NS("Video")];
    [o_video_decoded_lbl setStringValue: _NS("Decoded blocks")];
    [o_displayed_lbl setStringValue: _NS("Displayed frames")];
    [o_lost_frames_lbl setStringValue: _NS("Lost frames")];
103
	[o_fps_lbl setStringValue: _NS("Frames per Second")];
104

105 106 107 108
    [o_sout_box setTitle: _NS("Streaming")];
    [o_sent_packets_lbl setStringValue: _NS("Sent packets")];
    [o_sent_bytes_lbl setStringValue: _NS("Sent bytes")];
    [o_sent_bitrate_lbl setStringValue: _NS("Send rate")];
109

110 111 112 113
    [o_audio_box setTitle: _NS("Audio")];
    [o_audio_decoded_lbl setStringValue: _NS("Decoded blocks")];
    [o_played_abuffers_lbl setStringValue: _NS("Played buffers")];
    [o_lost_abuffers_lbl setStringValue: _NS("Lost buffers")];
114 115

    [o_info_window setInitialFirstResponder: o_uri_txt];
116 117
    [o_info_window setDelegate: self];

118
    /* We may be awoken from nib way after initialisation
119 120 121 122
     * Update ourselves */
    [self updatePanelWithItem:p_item];
}

123
- (void)stopTimers
124 125 126 127 128 129 130 131 132
{
    /* make sure that the timer is released in any case */
    if( o_statUpdateTimer && [o_statUpdateTimer isValid] )
        [o_statUpdateTimer invalidate];

    [rootItem release];

    [o_statUpdateTimer release];
    o_statUpdateTimer = nil;
Benjamin Pracht's avatar
Benjamin Pracht committed
133 134
}

135 136
- (void)dealloc
{
137
    /* make sure that the timer is released in any case */
138
    if( o_statUpdateTimer && [o_statUpdateTimer isValid] )
139 140
        [o_statUpdateTimer invalidate];

141 142 143
    [o_statUpdateTimer release];

    if( p_item ) vlc_gc_decref( p_item );
144

145 146
    [super dealloc];
}
Benjamin Pracht's avatar
Benjamin Pracht committed
147

148
- (void)windowDidBecomeKey:(NSNotification *)notification
149
{
150 151 152
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
    if( b_stats )
    {
153 154 155 156 157 158
        if( o_statUpdateTimer )
        {
            [o_statUpdateTimer invalidate];
            [o_statUpdateTimer release];
            o_statUpdateTimer = nil;
        }
159 160
        o_statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 1
            target: self selector: @selector(updateStatistics:)
161
            userInfo: nil repeats: YES];
162 163 164
        [o_statUpdateTimer fire];
        [o_statUpdateTimer retain];
    }
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
}

- (BOOL)windowShouldClose:(id)sender
{
    if( [o_statUpdateTimer isValid] )
        [o_statUpdateTimer invalidate];

    if( o_statUpdateTimer )
        [o_statUpdateTimer release];
    o_statUpdateTimer = nil;
    return YES;
}

- (void)initPanel
{
    BOOL b_stats = config_GetInt(VLCIntf, "stats");
    if( !b_stats )
182 183 184 185 186
    {
        if( [o_tab_view numberOfTabViewItems] > 2 )
            [o_tab_view removeTabViewItem: [o_tab_view tabViewItemAtIndex: 2]];
    }

187
    [self initMediaPanelStats];
188
    [o_info_window makeKeyAndOrderFront: self];
189 190
}

maxime Ripard's avatar
maxime Ripard committed
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
- (void)initMediaPanelStats
{
    //Initializing Input Variables
    [o_read_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
    [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];
    
    //Initializing Video Variables
    [o_video_decoded_txt setIntValue:0];
    [o_displayed_txt setIntValue:0];
    [o_lost_frames_txt setIntValue:0];
    [o_fps_txt setFloatValue:0];

    //Initializing Output Variables
    [o_sent_packets_txt setIntValue: 0];
    [o_sent_bytes_txt setStringValue: [NSString stringWithFormat:@"%8.0f kB", (float)0]];
    [o_sent_bitrate_txt setStringValue: [NSString stringWithFormat:@"%6.0f kb/s", (float)0]];

    //Initializing Audio Variables
    [o_audio_decoded_txt setIntValue:0];
    [o_played_abuffers_txt setIntValue: 0];
    [o_lost_abuffers_txt setIntValue: 0];

}

217
- (void)updatePanelWithItem:(input_item_t *)_p_item;
218
{
219
    if( _p_item != p_item )
220
    {
221
        if( p_item ) vlc_gc_decref( p_item );
222
        [o_saveMetaData_btn setEnabled: NO];
223 224
        if( _p_item ) vlc_gc_incref( _p_item );
        p_item = _p_item;
225
    }
226

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    if( !p_item )
    {
        /* Erase */
    #define SET( foo ) \
        [self setMeta: "" forLabel: o_##foo##_txt];
        SET( uri );
        SET( title );
        SET( author );
        SET( collection );
        SET( seqNum );
        SET( genre );
        SET( copyright );
        SET( publisher );
        SET( nowPlaying );
        SET( language );
        SET( date );
        SET( description );
    #undef SET
        [o_image_well setImage: [NSImage imageNamed: @"noart.png"]];
    }
247
    else
248 249 250
    {
        if( !input_item_IsPreparsed( p_item ) )
        {
Antoine Cellerier's avatar
Antoine Cellerier committed
251
            playlist_t * p_playlist = pl_Hold( VLCIntf );
252
            playlist_PreparseEnqueue( p_playlist, p_item, pl_Unlocked );
253 254 255
            pl_Release( VLCIntf );
        }

256 257 258
        /* fill uri info */
        char * psz_url = input_item_GetURI( p_item );
        [o_uri_txt setStringValue: [NSString stringWithUTF8String: psz_url ? psz_url : ""  ]];
259 260
        free( psz_url );

261 262 263 264 265 266 267
        /* fill title info */
        char * psz_title = input_item_GetTitle( p_item );
        if( !psz_title )
            psz_title = input_item_GetName( p_item );
        [o_title_txt setStringValue: [NSString stringWithUTF8String: psz_title ? : ""  ]];
        free( psz_title );

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    #define SET( foo, bar ) \
        char *psz_##foo = input_item_Get##bar ( p_item ); \
        [self setMeta: psz_##foo forLabel: o_##foo##_txt]; \
        FREENULL( psz_##foo );

        /* fill the other fields */
        SET( author, Artist );
        SET( collection, Album );
        SET( seqNum, TrackNum );
        SET( genre, Genre );
        SET( copyright, Copyright );
        SET( publisher, Publisher );
        SET( nowPlaying, NowPlaying );
        SET( language, Language );
        SET( date, Date );
        SET( description, Description );

    #undef SET

        char *psz_meta;
        NSImage *o_image;
        psz_meta = input_item_GetArtURL( p_item );
        if( psz_meta && !strncmp( psz_meta, "file://", 7 ) )
291
            o_image = [[NSImage alloc] initWithContentsOfFile: [NSString stringWithUTF8String: psz_meta+7]];
292 293 294 295 296 297
        else
            o_image = [[NSImage imageNamed: @"noart.png"] retain];
        [o_image_well setImage: o_image];
        [o_image release];
        FREENULL( psz_meta );
    }
298

299
    /* reload the advanced table */
300
    [rootItem refresh];
301
    [o_outline_view reloadData];
302

303 304
    /* update the stats once to display p_item change faster */
    [self updateStatistics: nil];
305

Benjamin Pracht's avatar
Benjamin Pracht committed
306 307
}

308
- (void)setMeta: (char *)psz_meta forLabel: (id)theItem
309 310
{
    if( psz_meta != NULL && *psz_meta)
311
        [theItem setStringValue: [NSString stringWithUTF8String:psz_meta]];
312 313
    else
        [theItem setStringValue: @""];
314 315
}

316
- (void)updateStatistics:(NSTimer*)theTimer
317
{
318
    if( !p_item || !p_item->p_stats )
319
    {
320 321
        [self initMediaPanelStats];
        return;
322
    }
323 324 325 326 327 328 329 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

    vlc_mutex_lock( &p_item->p_stats->lock );

    /* input */
    [o_read_bytes_txt setStringValue: [NSString stringWithFormat:
        @"%8.0f kB", (float)(p_item->p_stats->i_read_bytes)/1000]];
    [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:
        @"%6.0f kb/s", (float)(p_item->p_stats->f_input_bitrate)*8000]];
    [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:
        @"%8.0f kB", (float)(p_item->p_stats->i_demux_read_bytes)/1000]];
    [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:
        @"%6.0f kb/s", (float)(p_item->p_stats->f_demux_bitrate)*8000]];

    /* Video */
    [o_video_decoded_txt setIntValue: p_item->p_stats->i_decoded_video];
    [o_displayed_txt setIntValue: p_item->p_stats->i_displayed_pictures];
    [o_lost_frames_txt setIntValue: p_item->p_stats->i_lost_pictures];
    float f_fps = 0;
    /* FIXME: input_Control( p_item, INPUT_GET_VIDEO_FPS, &f_fps ); */
    [o_fps_txt setFloatValue: f_fps];

    /* Sout */
    [o_sent_packets_txt setIntValue: p_item->p_stats->i_sent_packets];
    [o_sent_bytes_txt setStringValue: [NSString stringWithFormat: @"%8.0f kB",
        (float)(p_item->p_stats->i_sent_bytes)/1000]];
    [o_sent_bitrate_txt setStringValue: [NSString stringWithFormat:
        @"%6.0f kb/s", (float)(p_item->p_stats->f_send_bitrate*8)*1000]];

    /* Audio */
    [o_audio_decoded_txt setIntValue: p_item->p_stats->i_decoded_audio];
    [o_played_abuffers_txt setIntValue: p_item->p_stats->i_played_abuffers];
    [o_lost_abuffers_txt setIntValue: p_item->p_stats->i_lost_abuffers];

    vlc_mutex_unlock( &p_item->p_stats->lock );
357 358
}

359 360 361 362 363
- (IBAction)metaFieldChanged:(id)sender
{
    [o_saveMetaData_btn setEnabled: YES];
}

364
- (IBAction)saveMetaData:(id)sender
Benjamin Pracht's avatar
Benjamin Pracht committed
365
{
Antoine Cellerier's avatar
Antoine Cellerier committed
366
    playlist_t * p_playlist = pl_Hold( VLCIntf );
Benjamin Pracht's avatar
Benjamin Pracht committed
367
    vlc_value_t val;
Benjamin Pracht's avatar
Benjamin Pracht committed
368

369
    if( !p_item ) goto error;
370

371 372
    meta_export_t p_export;
    p_export.p_item = p_item;
373

374 375 376 377
    /* we can write meta data only in a file */
    vlc_mutex_lock( &p_item->lock );
    int i_type = p_item->i_type;
    vlc_mutex_unlock( &p_item->lock );
378

379 380
    if( i_type != ITEM_TYPE_FILE )
        goto error;
381

382 383 384 385
    char *psz_uri_orig = input_item_GetURI( p_item );
    char *psz_uri = psz_uri_orig;
    if( !strncmp( psz_uri, "file://", 7 ) )
        psz_uri += 7; /* strlen("file://") = 7 */
386

387 388
    p_export.psz_file = strndup( psz_uri, PATH_MAX );
    free( psz_uri_orig );
389

390 391
    #define utf8( o_blub ) \
        [[o_blub stringValue] UTF8String]
Benjamin Pracht's avatar
Benjamin Pracht committed
392

393 394 395 396 397 398 399 400 401 402 403
    input_item_SetName( p_item, utf8( o_title_txt ) );
    input_item_SetTitle( p_item, utf8( o_title_txt ) );
    input_item_SetArtist( p_item, utf8( o_author_txt ) );
    input_item_SetAlbum( p_item, utf8( o_collection_txt ) );
    input_item_SetGenre( p_item, utf8( o_genre_txt ) );
    input_item_SetTrackNum( p_item, utf8( o_seqNum_txt ) );
    input_item_SetDate( p_item, utf8( o_date_txt ) );
    input_item_SetCopyright( p_item, utf8( o_copyright_txt ) );
    input_item_SetPublisher( p_item, utf8( o_publisher_txt ) );
    input_item_SetDescription( p_item, utf8( o_description_txt ) );
    input_item_SetLanguage( p_item, utf8( o_language_txt ) );
404

405 406
    PL_LOCK;
    p_playlist->p_private = &p_export;
407

408
    module_t *p_mod = module_need( p_playlist, "meta writer", NULL, false );
409
    if( p_mod )
410
        module_unneed( p_playlist, p_mod );
411 412 413 414 415 416 417 418 419 420 421 422 423
    PL_UNLOCK;

    val.b_bool = true;
    var_Set( p_playlist, "intf-change", val );
    [self updatePanelWithItem: p_item];

    pl_Release( VLCIntf );
    [o_saveMetaData_btn setEnabled: NO];
    return;

error:
    pl_Release( VLCIntf );
    NSRunAlertPanel(_NS("Error while saving meta"),
424
        _NS("VLC was unable to save the meta data."),
425 426 427
        _NS("OK"), nil, nil);
}

428 429 430
- (IBAction)downloadCoverArt:(id)sender
{
    playlist_t * p_playlist = pl_Hold( VLCIntf );
431
    if( p_item) playlist_AskForArtEnqueue( p_playlist, p_item, pl_Unlocked );
432 433 434
    pl_Release( VLCIntf );
}

435 436
- (input_item_t *)item
{
437
    if( p_item ) vlc_gc_incref( p_item );
438
    return p_item;
439 440
}

441 442
@end

443 444 445 446 447 448
@implementation VLCInfo (NSMenuValidation)

- (BOOL)validateMenuItem:(NSMenuItem *)o_mi
{
    BOOL bEnabled = TRUE;

449
    if( [[o_mi title] isEqualToString: _NS("Information")] )
450
    {
451
        return ![[[VLCMain sharedInstance] getPlaylist] isSelectionEmpty];
452 453
    }

454
    return TRUE;
455 456 457
}

@end
Benjamin Pracht's avatar
Benjamin Pracht committed
458

459
@implementation VLCInfo (NSTableDataSource)
460

461
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
462
{
463
    return (item == nil) ? [rootItem numberOfChildren] : [item numberOfChildren];
464 465 466 467 468 469
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
    return ([item numberOfChildren] > 0);
}

470
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
471
{
472
    return (item == nil) ? [rootItem childAtIndex:index] : (id)[item childAtIndex:index];
473 474 475 476 477 478
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    if ([[tableColumn identifier] isEqualToString:@"0"])
    {
479
        return (item == nil) ? @"" : (id)[item name];
480 481 482
    }
    else
    {
483
        return (item == nil) ? @"" : (id)[item value];
484 485 486
    }
}

487

488 489 490 491 492 493
@end

@implementation VLCInfoTreeItem

#define IsALeafNode ((id)-1)

494 495
- (id)initWithName: (NSString *)o_item_name value: (NSString *)o_item_value ID: (int)i_id
       parent:(VLCInfoTreeItem *)o_parent_item
496 497
{
    self = [super init];
498

499 500 501 502 503 504
    if( self != nil )
    {
        o_name = [o_item_name copy];
        o_value = [o_item_value copy];
        i_object_id = i_id;
        o_parent = o_parent_item;
505 506
        p_item = [[[VLCMain sharedInstance] getInfo] item];
        o_children = nil;
507 508 509 510
    }
    return( self );
}

511 512 513
- (id)init
{
    return [self initWithName:@"main" value:@"" ID:-1 parent:nil];
514 515 516 517
}

- (void)dealloc
{
518
    if( o_children != IsALeafNode ) [o_children release];
519
    [o_name release];
520 521
    [o_value release];
    if( p_item ) vlc_gc_decref( p_item );
522 523 524 525 526 527 528
    [super dealloc];
}

/* Creates and returns the array of children
 * Loads children incrementally */
- (NSArray *)children
{
529 530
    if( !p_item ) return nil;

531 532 533 534
    if (o_children == NULL)
    {
        int i;

535
        if( i_object_id == -1 )
536
        {
537 538 539
            vlc_mutex_lock( &p_item->lock );
            o_children = [[NSMutableArray alloc] initWithCapacity: p_item->i_categories];
            for (i = 0 ; i < p_item->i_categories ; i++)
540
            {
541 542 543 544
                NSString * name = [NSString stringWithUTF8String: p_item->pp_categories[i]->psz_name];
                VLCInfoTreeItem * item = [[VLCInfoTreeItem alloc] initWithName:name value:@"" ID:i parent:self];
                [item autorelease];
                [o_children addObject:item];
545
            }
546 547 548 549 550 551 552 553
            vlc_mutex_unlock( &p_item->lock );
        }
        else if( o_parent->i_object_id == -1 )
        {
            vlc_mutex_lock( &p_item->lock );
            info_category_t * cat = p_item->pp_categories[i_object_id];
            o_children = [[NSMutableArray alloc] initWithCapacity: cat->i_infos];
            for (i = 0 ; i < cat->i_infos ; i++)
554
            {
555 556 557 558 559
                NSString * name = [NSString stringWithUTF8String: cat->pp_infos[i]->psz_name];
                NSString * value = [NSString stringWithUTF8String: cat->pp_infos[i]->psz_value ? : ""];
                VLCInfoTreeItem * item = [[VLCInfoTreeItem alloc] initWithName:name value:value ID:i parent:self];
                [item autorelease];
                [o_children addObject:item];
560
            }
561 562 563 564 565
            vlc_mutex_unlock( &p_item->lock );
        }
        else
        {
            o_children = IsALeafNode;
566 567 568 569 570
        }
    }
    return o_children;
}

571
- (NSString *)name
572
{
573
    return [[o_name retain] autorelease];
574 575
}

576
- (NSString *)value
577
{
578
    return [[o_value retain] autorelease];
579 580 581 582
}

- (void)refresh
{
583
    input_item_t * oldItem = p_item;
584
    p_item = [[[VLCMain sharedInstance] getInfo] item];
585 586
    if( oldItem && oldItem != p_item ) vlc_gc_decref( oldItem );

587 588
    [o_children release];
    o_children = nil;
589
}
Benjamin Pracht's avatar
Benjamin Pracht committed
590

591 592 593 594 595 596 597 598 599 600
- (VLCInfoTreeItem *)childAtIndex:(int)i_index {
    return [[self children] objectAtIndex:i_index];
}

- (int)numberOfChildren {

    id i_tmp = [self children];
    return ( i_tmp == IsALeafNode ) ? (-1) : (int)[i_tmp count];
}

Benjamin Pracht's avatar
Benjamin Pracht committed
601 602
@end