]> git.sesse.net Git - vlc/blob - modules/gui/macosx/playlistinfo.m
macosx: playlistinfo.m. small typo which caused ObjC exception and froze interface...
[vlc] / modules / gui / macosx / playlistinfo.m
1 /*****************************************************************************
2  r playlistinfo.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Benjamin Pracht <bigben at videolan dot org>
8  *          Felix Kühne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #include "intf.h"
30 #include "playlistinfo.h"
31 #include "playlist.h"
32
33 /*****************************************************************************
34  * VLCPlaylistInfo Implementation
35  *****************************************************************************/
36
37 @implementation VLCInfo
38
39 - (id)init
40 {
41     self = [super init];
42
43     if( self != nil )
44     {
45         p_item = NULL;
46         o_statUpdateTimer = nil;
47     }
48     return( self );
49 }
50
51 - (void)awakeFromNib
52 {
53     [o_info_window setExcludedFromWindowsMenu: TRUE];
54
55     [o_info_window setTitle: _NS("Information")];
56     [o_uri_lbl setStringValue: _NS("URI")];
57     [o_title_lbl setStringValue: _NS("Title")];
58     [o_author_lbl setStringValue: _NS("Author")];
59     [o_btn_ok setTitle: _NS("OK")];
60     [o_btn_cancel setTitle: _NS("Cancel")];
61  
62     [[o_tab_view tabViewItemAtIndex: 0] setLabel: _NS("General")];
63     [[o_tab_view tabViewItemAtIndex: 1] setLabel: _NS("Advanced Information")];
64     [[o_tab_view tabViewItemAtIndex: 2] setLabel: _NS("Statistics")];
65     [o_tab_view selectTabViewItemAtIndex: 0];
66
67     /* constants defined in vlc_meta.h */
68     [o_genre_lbl setStringValue: _NS(VLC_META_GENRE)];
69     [o_copyright_lbl setStringValue: _NS(VLC_META_COPYRIGHT)];
70     [o_collection_lbl setStringValue: _NS(VLC_META_COLLECTION)];
71     [o_seqNum_lbl setStringValue: _NS(VLC_META_SEQ_NUM)];
72     [o_description_lbl setStringValue: _NS(VLC_META_DESCRIPTION)];
73     [o_rating_lbl setStringValue: _NS(VLC_META_RATING)];
74     [o_date_lbl setStringValue: _NS(VLC_META_DATE)];
75     [o_language_lbl setStringValue: _NS(VLC_META_LANGUAGE)];
76     [o_nowPlaying_lbl setStringValue: _NS(VLC_META_NOW_PLAYING)];
77     [o_publisher_lbl setStringValue: _NS(VLC_META_PUBLISHER)];
78  
79     /* statistics */
80     [o_input_box setTitle: _NS("Input")];
81     [o_read_bytes_lbl setStringValue: _NS("Read at media")];
82     [o_input_bitrate_lbl setStringValue: _NS("Input bitrate")];
83     [o_demux_bytes_lbl setStringValue: _NS("Demuxed")];
84     [o_demux_bitrate_lbl setStringValue: _NS("Stream bitrate")];
85  
86     [o_video_box setTitle: _NS("Video")];
87     [o_video_decoded_lbl setStringValue: _NS("Decoded blocks")];
88     [o_displayed_lbl setStringValue: _NS("Displayed frames")];
89     [o_lost_frames_lbl setStringValue: _NS("Lost frames")];
90         [o_fps_lbl setStringValue: _NS("Frames per Second")];
91  
92     [o_sout_box setTitle: _NS("Streaming")];
93     [o_sent_packets_lbl setStringValue: _NS("Sent packets")];
94     [o_sent_bytes_lbl setStringValue: _NS("Sent bytes")];
95     [o_sent_bitrate_lbl setStringValue: _NS("Send rate")];
96  
97     [o_audio_box setTitle: _NS("Audio")];
98     [o_audio_decoded_lbl setStringValue: _NS("Decoded blocks")];
99     [o_played_abuffers_lbl setStringValue: _NS("Played buffers")];
100     [o_lost_abuffers_lbl setStringValue: _NS("Lost buffers")];
101 }
102
103 - (void)dealloc
104 {
105     /* make sure that the timer is released in any case */
106     if( [o_statUpdateTimer isValid] )
107         [o_statUpdateTimer invalidate];
108
109     if ( o_statUpdateTimer )
110         [o_statUpdateTimer release];
111
112     [super dealloc];
113 }
114
115 #if GC_ENABLED
116 - (void)finalize
117 {
118     /* since dealloc isn't called with enabled GC on 10.5, we need this to prevent core crashes */
119     if( [o_statUpdateTimer isValid] )
120         [o_statUpdateTimer invalidate];
121     [super finalize];
122 }
123 #endif
124  
125 - (IBAction)togglePlaylistInfoPanel:(id)sender
126 {
127     if( [o_info_window isVisible] )
128     {
129         [self windowShouldClose: nil];
130         [o_info_window orderOut: sender];
131     }
132     else
133     {
134         p_item = [[[VLCMain sharedInstance] getPlaylist] selectedPlaylistItem];
135         [self initPanel:sender];
136     }
137 }
138
139 - (IBAction)toggleInfoPanel:(id)sender
140 {
141     if( [o_info_window isVisible] )
142     {
143         [self windowShouldClose: nil];
144         [o_info_window orderOut: sender];
145     }
146     else
147     {
148         intf_thread_t * p_intf = VLCIntf;
149         playlist_t * p_playlist = pl_Yield( p_intf );
150
151         p_item = p_playlist->status.p_item;
152         vlc_object_release( p_playlist );
153  
154         [self initPanel:sender];
155     }
156 }
157
158 - (void)initPanel:(id)sender
159 {
160     BOOL b_stats = config_GetInt(VLCIntf, "stats");
161     if( b_stats )
162     {
163         o_statUpdateTimer = [NSTimer scheduledTimerWithTimeInterval: 1
164             target: self selector: @selector(updateStatistics:)
165             userInfo: nil repeats: YES];
166         [o_statUpdateTimer fire];
167         [o_statUpdateTimer retain];
168     }
169     else
170     {
171         if( [o_tab_view numberOfTabViewItems] > 2 )
172             [o_tab_view removeTabViewItem: [o_tab_view tabViewItemAtIndex: 2]];
173     }
174
175     [self updatePanel];
176     [o_info_window makeKeyAndOrderFront: sender];
177 }
178
179 - (void)updatePanel
180 {
181     /* make sure that we got the current item and not an outdated one */
182     intf_thread_t * p_intf = VLCIntf;
183         playlist_t * p_playlist = pl_Yield( p_intf );
184
185     p_item = p_playlist->status.p_item;
186     vlc_object_release( p_playlist );
187
188     /* check whether our item is valid, because we would crash if not */
189     if(! [self isItemInPlaylist: p_item] ) return;
190
191     /* fill uri info */
192     char *psz_uri = input_item_GetURI( p_item->p_input );
193     if( psz_uri )
194     {
195         [o_uri_txt setStringValue:
196             ([NSString stringWithUTF8String:psz_uri] == nil ) ?
197             [NSString stringWithCString:psz_uri] :
198             [NSString stringWithUTF8String:psz_uri]];
199     }
200     free( psz_uri );
201
202 #define SET( foo, bar ) \
203     char *psz_##foo = input_item_Get##bar ( p_item->p_input ); \
204     [self setMeta: psz_##foo forLabel: o_##foo##_txt]; \
205     free( psz_##foo );
206
207     /* fill the other fields */
208     SET( title, Title );
209     SET( author, Artist );
210     SET( collection, Album );
211     SET( seqNum, TrackNum );
212     SET( genre, Genre );
213     SET( copyright, Copyright );
214     SET( rating, Rating );
215     SET( publisher, Publisher );
216     SET( nowPlaying, NowPlaying );
217     SET( language, Language );
218     SET( date, Date );
219
220 #undef SET
221
222     /* reload the advanced table */
223     [[VLCInfoTreeItem rootItem] refresh];
224     [o_outline_view reloadData];
225
226     /* update the stats once to display p_item change faster */
227     [self updateStatistics: nil];
228 }
229
230 - (void)setMeta: (char *)psz_meta forLabel: (id)theItem
231 {
232     if( psz_meta != NULL && *psz_meta)
233         [theItem setStringValue:
234             ([NSString stringWithUTF8String:psz_meta] == nil ) ?
235             [NSString stringWithCString:psz_meta] :
236             [NSString stringWithUTF8String:psz_meta]];
237     else
238         [theItem setStringValue: @"-"];
239 }
240
241 - (void)updateStatistics:(NSTimer*)theTimer
242 {
243     if( [self isItemInPlaylist: p_item] )
244     {
245         /* we can only do that if there's a valid input around */
246
247         vlc_mutex_lock( &p_item->p_input->p_stats->lock );
248
249         /* input */
250         [o_read_bytes_txt setStringValue: [NSString stringWithFormat:
251             @"%8.0f kB", (float)(p_item->p_input->p_stats->i_read_bytes)/1000]];
252         [o_input_bitrate_txt setStringValue: [NSString stringWithFormat:
253             @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_input_bitrate)*8000]];
254         [o_demux_bytes_txt setStringValue: [NSString stringWithFormat:
255             @"%8.0f kB", (float)(p_item->p_input->p_stats->i_demux_read_bytes)/1000]];
256         [o_demux_bitrate_txt setStringValue: [NSString stringWithFormat:
257             @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_demux_bitrate)*8000]];
258
259         /* Video */
260         [o_video_decoded_txt setStringValue: [NSString stringWithFormat: @"%5i",
261             p_item->p_input->p_stats->i_decoded_video]];
262         [o_displayed_txt setStringValue: [NSString stringWithFormat: @"%5i",
263             p_item->p_input->p_stats->i_displayed_pictures]];
264         [o_lost_frames_txt setStringValue: [NSString stringWithFormat: @"%5i",
265             p_item->p_input->p_stats->i_lost_pictures]];
266
267         /* Sout */
268         [o_sent_packets_txt setStringValue: [NSString stringWithFormat: @"%5i",
269             p_item->p_input->p_stats->i_sent_packets]];
270         [o_sent_bytes_txt setStringValue: [NSString stringWithFormat: @"%8.0f kB",
271             (float)(p_item->p_input->p_stats->i_sent_bytes)/1000]];
272         [o_sent_bitrate_txt setStringValue: [NSString stringWithFormat:
273             @"%6.0f kb/s", (float)(p_item->p_input->p_stats->f_send_bitrate*8)*1000]];
274
275         /* Audio */
276         [o_audio_decoded_txt setStringValue: [NSString stringWithFormat: @"%5i",
277             p_item->p_input->p_stats->i_decoded_audio]];
278         [o_played_abuffers_txt setStringValue: [NSString stringWithFormat: @"%5i",
279             p_item->p_input->p_stats->i_played_abuffers]];
280         [o_lost_abuffers_txt setStringValue: [NSString stringWithFormat: @"%5i",
281             p_item->p_input->p_stats->i_lost_abuffers]];
282
283         vlc_mutex_unlock( &p_item->p_input->p_stats->lock );
284     }
285 }
286
287 - (IBAction)infoCancel:(id)sender
288 {
289     [self windowShouldClose: nil];
290     [o_info_window orderOut: self];
291 }
292
293
294 - (IBAction)infoOk:(id)sender
295 {
296     intf_thread_t * p_intf = VLCIntf;
297     playlist_t * p_playlist = pl_Yield( p_intf );
298     vlc_value_t val;
299
300     if( [self isItemInPlaylist: p_item] )
301     {
302         input_item_SetName( p_item->p_input, (char*)
303                 [[o_title_txt stringValue] UTF8String] );
304         input_item_SetURI( p_item->p_input, (char*)
305                 [[o_uri_txt stringValue] UTF8String] );
306         input_item_SetArtist( p_item->p_input, (char*)
307                 [[o_author_txt stringValue] UTF8String] );
308
309         val.b_bool = VLC_TRUE;
310         var_Set( p_playlist, "intf-change", val );
311     }
312     vlc_object_release( p_playlist );
313     [self windowShouldClose: nil];
314     [o_info_window orderOut: self];
315 }
316
317 - (playlist_item_t *)getItem
318 {
319     return p_item;
320 }
321
322 - (BOOL)isItemInPlaylist:(playlist_item_t *)p_local_item
323 {
324     intf_thread_t * p_intf = VLCIntf;
325     playlist_t * p_playlist = pl_Yield( p_intf );
326     int i;
327
328     for( i = 0 ; i < p_playlist->all_items.i_size ; i++ )
329     {
330         if( ARRAY_VAL( p_playlist->all_items, i ) == p_local_item )
331         {
332             vlc_object_release( p_playlist );
333             return YES;
334         }
335     }
336     vlc_object_release( p_playlist );
337     return NO;
338 }
339
340 - (BOOL)windowShouldClose:(id)sender
341 {
342     if( [o_statUpdateTimer isValid] )
343         [o_statUpdateTimer invalidate];
344
345     if( o_statUpdateTimer )
346         [o_statUpdateTimer release];
347
348     return YES;
349 }
350
351 @end
352
353 @implementation VLCInfo (NSMenuValidation)
354
355 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
356 {
357     BOOL bEnabled = TRUE;
358
359     intf_thread_t * p_intf = VLCIntf;
360     input_thread_t * p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
361                                                        FIND_ANYWHERE );
362
363     if( [[o_mi title] isEqualToString: _NS("Information")] )
364     {
365         if( p_input == NULL )
366         {
367             bEnabled = FALSE;
368         }
369     }
370     if( p_input ) vlc_object_release( p_input );
371
372     return( bEnabled );
373 }
374
375 @end
376
377 @implementation VLCInfo (NSTableDataSource)
378
379 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
380 {
381     return (item == nil) ? [[VLCInfoTreeItem rootItem] numberOfChildren] : [item numberOfChildren];
382 }
383
384 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
385     return ([item numberOfChildren] > 0);
386 }
387
388 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
389 {
390     return (item == nil) ? [[VLCInfoTreeItem rootItem] childAtIndex:index] : (id)[item childAtIndex:index];
391 }
392
393 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
394 {
395     if ([[tableColumn identifier] isEqualToString:@"0"])
396     {
397         return (item == nil) ? @"" : (id)[item getName];
398     }
399     else
400     {
401         return (item == nil) ? @"" : (id)[item getValue];
402     }
403 }
404
405
406 @end
407
408 @implementation VLCInfoTreeItem
409
410 static VLCInfoTreeItem *o_root_item = nil;
411
412 #define IsALeafNode ((id)-1)
413
414 - (id)initWithName: (NSString *)o_item_name value: (NSString *)o_item_value ID: (int)i_id parent:(VLCInfoTreeItem *)o_parent_item
415 {
416     self = [super init];
417
418     if( self != nil )
419     {
420         o_name = [o_item_name copy];
421         o_value = [o_item_value copy];
422         i_object_id = i_id;
423         o_parent = o_parent_item;
424         if( [[VLCMain sharedInstance] getInfo] != nil )
425             p_item = [[[VLCMain sharedInstance] getInfo] getItem];
426         else
427             p_item = NULL;
428     }
429     return( self );
430 }
431
432 + (VLCInfoTreeItem *)rootItem {
433     if( o_root_item == nil )
434         o_root_item = [[VLCInfoTreeItem alloc] initWithName:@"main" value: @"" ID: 0 parent:nil];
435     return o_root_item;
436 }
437
438 - (void)dealloc
439 {
440     if( o_children != IsALeafNode ) [o_children release];
441     [o_name release];
442     [super dealloc];
443 }
444
445 /* Creates and returns the array of children
446  * Loads children incrementally */
447 - (NSArray *)children
448 {
449     if (o_children == NULL)
450     {
451         int i;
452
453         if( [[[VLCMain sharedInstance] getInfo] isItemInPlaylist: p_item] )
454         {
455             if( self == o_root_item )
456             {
457                 vlc_mutex_lock( &p_item->p_input->lock );
458                 o_children = [[NSMutableArray alloc] initWithCapacity:
459                                                 p_item->p_input->i_categories];
460                 for (i = 0 ; i < p_item->p_input->i_categories ; i++)
461                 {
462                     [o_children addObject:[[VLCInfoTreeItem alloc]
463                         initWithName: [NSString stringWithUTF8String:
464                             p_item->p_input->pp_categories[i]->psz_name]
465                         value: @""
466                         ID: i
467                         parent: self]];
468                 }
469                 vlc_mutex_unlock( &p_item->p_input->lock );
470             }
471             else if( o_parent == o_root_item )
472             {
473                 vlc_mutex_lock( &p_item->p_input->lock );
474                 o_children = [[NSMutableArray alloc] initWithCapacity:
475                     p_item->p_input->pp_categories[i_object_id]->i_infos];
476
477                 for (i = 0 ; i < p_item->p_input->pp_categories[i_object_id]->i_infos ; i++)
478                 {
479                     [o_children addObject:[[VLCInfoTreeItem alloc]
480                     initWithName: [NSString stringWithUTF8String:
481                             p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_name]
482                         value: [NSString stringWithUTF8String:
483                             p_item->p_input->pp_categories[i_object_id]->pp_infos[i]->psz_value]
484                         ID: i
485                         parent: self]];
486                 }
487                 vlc_mutex_unlock( &p_item->p_input->lock );
488             }
489             else
490             {
491                 o_children = IsALeafNode;
492             }
493         }
494     }
495     return o_children;
496 }
497
498 - (NSString *)getName
499 {
500     return o_name;
501 }
502
503 - (NSString *)getValue
504 {
505     return o_value;
506 }
507
508 - (VLCInfoTreeItem *)childAtIndex:(int)i_index {
509     return [[self children] objectAtIndex:i_index];
510 }
511
512 - (int)numberOfChildren {
513     id i_tmp = [self children];
514     return ( i_tmp == IsALeafNode ) ? (-1) : (int)[i_tmp count];
515 }
516
517 /*- (int)selectedPlaylistItem
518 {
519     return i_item;
520 }
521 */
522 - (void)refresh
523 {
524     p_item = [[[VLCMain sharedInstance] getInfo] getItem];
525     if( o_children != NULL )
526     {
527         [o_children release];
528         o_children = NULL;
529     }
530 }
531
532 @end
533