]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* OS X: Fixed a segfault with empty configuration strings (closes #65),
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: intf.m,v 1.21 2003/01/09 23:43:07 massiot Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <sys/param.h>                                    /* for MAXPATHLEN */
30 #include <string.h>
31
32 #include <QuickTime/QuickTime.h>
33
34 #include "intf.h"
35 #include "vout.h"
36 #include "prefs.h"
37 #include "playlist.h"
38
39 /*****************************************************************************
40  * Local prototypes.
41  *****************************************************************************/
42 static void Run       ( intf_thread_t *p_intf );
43
44 /*****************************************************************************
45  * OpenIntf: initialize interface
46  *****************************************************************************/
47 int E_(OpenIntf) ( vlc_object_t *p_this )
48 {   
49     intf_thread_t *p_intf = (intf_thread_t*) p_this;
50
51     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
52     if( p_intf->p_sys == NULL )
53     {
54         return( 1 );
55     }
56
57     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
58
59     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
60     p_intf->p_sys->o_sendport = [[NSPort port] retain];
61
62     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
63
64     p_intf->pf_run = Run;
65
66     [[VLCApplication sharedApplication] autorelease];
67     [NSApp initIntlSupport];
68     [NSApp setIntf: p_intf];
69
70     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
71
72     return( 0 );
73 }
74
75 /*****************************************************************************
76  * CloseIntf: destroy interface
77  *****************************************************************************/
78 void E_(CloseIntf) ( vlc_object_t *p_this )
79 {
80     intf_thread_t *p_intf = (intf_thread_t*) p_this;
81
82     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
83
84     [p_intf->p_sys->o_sendport release];
85     [p_intf->p_sys->o_pool release];
86
87     free( p_intf->p_sys );
88 }
89
90 /*****************************************************************************
91  * Run: main loop
92  *****************************************************************************/
93 static void Run( intf_thread_t *p_intf )
94 {
95     [NSApp run];
96 }
97
98 /*****************************************************************************
99  * VLCApplication implementation 
100  *****************************************************************************/
101 @implementation VLCApplication
102
103 - (id)init
104 {
105     /* default encoding: ISO-8859-1 */
106     i_encoding = NSISOLatin1StringEncoding;
107
108     return( [super init] );
109 }
110
111 - (void)initIntlSupport
112 {
113     char *psz_lang = getenv( "LANG" );
114
115     if( psz_lang == NULL )
116     {
117         return;
118     }
119
120     if( strncmp( psz_lang, "pl", 2 ) == 0 )
121     {
122         i_encoding = NSISOLatin2StringEncoding;
123     }
124     else if( strncmp( psz_lang, "ja", 2 ) == 0 ) 
125     {
126         i_encoding = NSJapaneseEUCStringEncoding;
127     }
128     else if( strncmp( psz_lang, "ru", 2 ) == 0 )
129     {
130 #define CFSENC2NSSENC(e) CFStringConvertEncodingToNSStringEncoding(e)
131         i_encoding = CFSENC2NSSENC( kCFStringEncodingKOI8_R ); 
132 #undef CFSENC2NSSENC
133     }
134 }
135
136 - (NSString *)localizedString:(char *)psz
137 {
138     if ( psz == NULL ) return NULL;
139     UInt32 uiLength = (UInt32)strlen( psz );
140     NSData * o_data = [NSData dataWithBytes: psz length: uiLength];
141     NSString *o_str = [[NSString alloc] initWithData: o_data
142                                         encoding: i_encoding];
143     return( [o_str autorelease] );
144 }
145
146 - (void)setIntf:(intf_thread_t *)_p_intf
147 {
148     p_intf = _p_intf;
149 }
150
151 - (intf_thread_t *)getIntf
152 {
153     return( p_intf );
154 }
155
156 - (void)terminate:(id)sender
157 {
158     [self getIntf]->p_vlc->b_die = VLC_TRUE;
159 }
160
161 @end
162
163 /*****************************************************************************
164  * VLCMain implementation 
165  *****************************************************************************/
166 @implementation VLCMain
167
168 - (id)init
169 {
170     self = [super init];
171
172     if( self != nil )
173     {
174         o_prefs = nil;
175     }
176
177     return( self ); 
178 }
179
180 - (void)awakeFromNib
181 {
182     NSString * pTitle = [NSString
183         stringWithCString: VOUT_TITLE " (Cocoa)"];
184
185     [o_window setTitle: pTitle];
186
187     /* button controls */
188     [o_btn_playlist setToolTip: _NS("Playlist")];
189     [o_btn_prev setToolTip: _NS("Previous")];
190     [o_btn_slower setToolTip: _NS("Slower")];
191     [o_btn_play setToolTip: _NS("Play")];
192     [o_btn_pause setToolTip: _NS("Pause")];
193     [o_btn_stop setToolTip: _NS("Stop")];
194     [o_btn_faster setToolTip: _NS("Faster")];
195     [o_btn_next setToolTip: _NS("Next")];
196     [o_btn_prefs setToolTip: _NS("Preferences")];
197
198     /* messages panel */ 
199     [o_msgs_panel setTitle: _NS("Messages")];
200     [o_msgs_btn_ok setTitle: _NS("Close")];
201
202     /* main menu */
203     [o_mi_about setTitle: _NS("About VLC Media Player")];
204     [o_mi_prefs setTitle: _NS("Preferences")];
205     [o_mi_hide setTitle: _NS("Hide VLC")];
206     [o_mi_hide_others setTitle: _NS("Hide Others")];
207     [o_mi_show_all setTitle: _NS("Show All")];
208     [o_mi_quit setTitle: _NS("Quit VLC")];
209
210     [o_mu_file setTitle: _NS("File")];
211     [o_mi_open_generic setTitle: _NS("Open...")];
212     [o_mi_open_file setTitle: _NS("Open File...")];
213     [o_mi_open_disc setTitle: _NS("Open Disc...")];
214     [o_mi_open_net setTitle: _NS("Open Network...")];
215     [o_mi_open_recent setTitle: _NS("Open Recent")];
216     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
217
218     [o_mu_edit setTitle: _NS("Edit")];
219     [o_mi_cut setTitle: _NS("Cut")];
220     [o_mi_copy setTitle: _NS("Copy")];
221     [o_mi_paste setTitle: _NS("Paste")];
222     [o_mi_clear setTitle: _NS("Clear")];
223     [o_mi_select_all setTitle: _NS("Select All")];
224
225     [o_mu_view setTitle: _NS("View")];
226     [o_mi_playlist setTitle: _NS("Playlist")];
227     [o_mi_messages setTitle: _NS("Messages")];
228
229     [o_mu_controls setTitle: _NS("Controls")];
230     [o_mi_play setTitle: _NS("Play")];
231     [o_mi_pause setTitle: _NS("Pause")];
232     [o_mi_stop setTitle: _NS("Stop")];
233     [o_mi_faster setTitle: _NS("Faster")];
234     [o_mi_slower setTitle: _NS("Slower")];
235     [o_mi_previous setTitle: _NS("Previous")];
236     [o_mi_next setTitle: _NS("Next")];
237     [o_mi_loop setTitle: _NS("Loop")];
238     [o_mi_vol_up setTitle: _NS("Volume Up")];
239     [o_mi_vol_down setTitle: _NS("Volume Down")];
240     [o_mi_mute setTitle: _NS("Mute")];
241     [o_mi_channels setTitle: _NS("Channels")];
242     [o_mi_device setTitle: _NS("Device")];
243     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
244     [o_mi_screen setTitle: _NS("Screen")];
245     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
246     [o_mi_program setTitle: _NS("Program")];
247     [o_mi_title setTitle: _NS("Title")];
248     [o_mi_chapter setTitle: _NS("Chapter")];
249     [o_mi_language setTitle: _NS("Language")];
250     [o_mi_subtitle setTitle: _NS("Subtitles")];
251
252     [o_mu_window setTitle: _NS("Window")];
253     [o_mi_minimize setTitle: _NS("Minimize Window")];
254     [o_mi_close_window setTitle: _NS("Close Window")];
255     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
256
257     /* dock menu */
258     [o_dmi_play setTitle: _NS("Play")];
259     [o_dmi_pause setTitle: _NS("Pause")];
260     [o_dmi_stop setTitle: _NS("Stop")];
261
262     /* error panel */
263     [o_error setTitle: _NS("Error")];
264     [o_err_lbl setStringValue: _NS("An error has occurred which probably prevented the execution of your request :")];
265     [o_err_bug_lbl setStringValue: _NS("If you believe that it is a bug, please follow the instructions at :")];
266     [o_err_btn_msgs setTitle: _NS("Open Messages Window")];
267     [o_err_btn_dismiss setTitle: _NS("Dismiss")];
268
269     [self manageMode];
270 }
271
272 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
273 {
274     intf_thread_t * p_intf = [NSApp getIntf];
275
276     f_slider_old = f_slider = 0.0;
277     o_slider_lock = [[NSLock alloc] init];
278
279     [NSThread detachNewThreadSelector: @selector(manage)
280         toTarget: self withObject: nil];
281
282     [p_intf->p_sys->o_sendport setDelegate: self];
283     [[NSRunLoop currentRunLoop] 
284         addPort: p_intf->p_sys->o_sendport
285         forMode: NSDefaultRunLoopMode];
286 }
287
288 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
289 {
290     [o_playlist appendArray:
291         [NSArray arrayWithObject: o_filename] atPos: -1];
292
293     return( TRUE );
294 }
295
296 - (void)manage
297 {
298     NSDate * o_sleep_date;
299     intf_thread_t * p_intf = [NSApp getIntf];
300     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
301
302     while( !p_intf->b_die )
303     {
304         int i_start, i_stop;
305
306         vlc_mutex_lock( &p_intf->change_lock );
307
308         /* update the input */
309         if( p_intf->p_sys->p_input == NULL )
310         {
311             p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
312                                                               FIND_ANYWHERE );
313         }
314         else if( p_intf->p_sys->p_input->b_dead )
315         {
316             vlc_object_release( p_intf->p_sys->p_input );
317             p_intf->p_sys->p_input = NULL;
318         }
319
320         if( p_intf->p_sys->p_input != NULL && !p_intf->p_sys->p_input->b_die )
321         {
322             vlc_bool_t b_need_menus = 0;
323             input_thread_t * p_input = p_intf->p_sys->p_input;
324             aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
325                                                         FIND_ANYWHERE );
326             vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
327                                                       FIND_ANYWHERE );
328
329             /* Disable screen saver. */
330             UpdateSystemActivity( UsrActivity );
331
332             vlc_mutex_lock( &p_input->stream.stream_lock );
333
334             [self displayTime];
335
336             /* New input or stream map change */
337             if( p_input->stream.b_changed )
338             {
339                 [self manageMode];
340                 b_need_menus = 1;
341                 p_intf->p_sys->b_playing = 1;
342             }
343
344             if( p_intf->p_sys->i_part !=
345                 p_input->stream.p_selected_area->i_part )
346             {
347                 p_intf->p_sys->b_chapter_update = 1;
348                 b_need_menus = 1;
349             }
350
351             if ( p_aout != NULL )
352             {
353                 vlc_value_t val;
354                 if ( var_Get( (vlc_object_t *)p_aout, "intf-change", &val )
355                       >= 0 && val.b_bool )
356                 {
357                     p_intf->p_sys->b_aout_update = 1;
358                     b_need_menus = 1;
359                 }
360                 vlc_object_release( (vlc_object_t *)p_aout );
361             }
362
363             if ( p_vout != NULL )
364             {
365                 vlc_value_t val;
366                 if ( var_Get( (vlc_object_t *)p_vout, "intf-change", &val )
367                       >= 0 && val.b_bool )
368                 {
369                     p_intf->p_sys->b_vout_update = 1;
370                     b_need_menus = 1;
371                 }
372                 vlc_object_release( (vlc_object_t *)p_vout );
373             }
374
375             if ( b_need_menus )
376                 [self setupMenus];
377
378             vlc_mutex_unlock( &p_input->stream.stream_lock );
379         }
380         else if( p_intf->p_sys->b_playing && !p_intf->b_die )
381         {
382             [self displayTime];
383             [self manageMode];
384             p_intf->p_sys->b_playing = 0;
385
386             if ( p_intf->p_sys->b_stopping )
387             {
388                 vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
389                                                           FIND_ANYWHERE );
390
391                 if ( p_vout != NULL )
392                 {
393                     vlc_object_detach( p_vout );
394                     vlc_object_release( p_vout );
395                     vout_Destroy( p_vout );
396                 }
397                 p_intf->p_sys->b_stopping = 0;
398             }
399         }
400
401         /* update the log window */
402         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
403         i_stop = *p_intf->p_sys->p_sub->pi_stop;
404         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
405
406         if( p_intf->p_sys->p_sub->i_start != i_stop )
407         {
408             NSColor *o_white = [NSColor whiteColor];
409             NSColor *o_red = [NSColor redColor];
410             NSColor *o_yellow = [NSColor yellowColor];
411             NSColor *o_gray = [NSColor grayColor];
412
413             unsigned int ui_length = [[o_messages string] length];
414
415             NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
416             static const char * ppsz_type[4] = { ": ", " error: ", 
417                                                  " warning: ", " debug: " }; 
418         
419             [o_messages setEditable: YES];
420             [o_messages setSelectedRange: NSMakeRange( ui_length, 0 )];
421             [o_messages scrollRangeToVisible: NSMakeRange( ui_length, 0 )];
422
423             for( i_start = p_intf->p_sys->p_sub->i_start;
424                  i_start != i_stop;
425                  i_start = (i_start+1) % VLC_MSG_QSIZE )
426             {
427                 NSString *o_msg;
428                 NSDictionary *o_attr;
429                 NSAttributedString *o_msg_color;
430                 int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
431
432                 o_attr = [NSDictionary dictionaryWithObject: o_gray
433                     forKey: NSForegroundColorAttributeName];
434                 o_msg = [NSString stringWithFormat: @"%s%s",
435                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module, 
436                     ppsz_type[i_type]];
437                 o_msg_color = [[NSAttributedString alloc]
438                     initWithString: o_msg attributes: o_attr];
439                 [o_messages insertText: o_msg_color];
440
441                 o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
442                     forKey: NSForegroundColorAttributeName];
443                 o_msg = [NSString stringWithCString:
444                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
445                 o_msg_color = [[NSAttributedString alloc]
446                     initWithString: o_msg attributes: o_attr];
447                 [o_messages insertText: o_msg_color];
448
449                 [o_messages insertText: @"\n"];
450
451                 if ( i_type == 1 )
452                 {
453                     /* Error panel */
454                     NSString *o_my_msg =
455                         [NSString stringWithFormat: @"%s: %s\n",
456                          p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
457                          p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
458
459                     [o_err_msg setEditable: YES];
460                     [o_err_msg setSelectedRange:
461                                 NSMakeRange( [[o_err_msg string] length], 0 )];
462                     [o_err_msg insertText: o_my_msg];
463
464                     [o_error makeKeyAndOrderFront: self];
465                     [o_err_msg setEditable: NO];
466                 }
467             }
468
469             [o_messages setEditable: NO];
470
471             vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
472             p_intf->p_sys->p_sub->i_start = i_start;
473             vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
474         }
475
476         vlc_mutex_unlock( &p_intf->change_lock );
477
478         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: 0.1];
479         [NSThread sleepUntilDate: o_sleep_date];
480     }
481
482     [self terminate];
483
484     [o_pool release];
485 }
486
487 - (void)terminate
488 {
489     NSEvent * pEvent;
490     vout_thread_t * p_vout;
491     playlist_t * p_playlist;
492     intf_thread_t * p_intf = [NSApp getIntf];
493
494     /* release input */
495     if( p_intf->p_sys->p_input )
496     {
497         vlc_object_release( p_intf->p_sys->p_input );
498         p_intf->p_sys->p_input = NULL;
499     }
500
501     if( o_prefs != nil )
502     {
503         [o_prefs release];
504         o_prefs = nil;
505     }
506
507     /*
508      * Free playlists
509      */
510     msg_Dbg( p_intf, "removing all playlists" );
511     while( (p_playlist = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_PLAYLIST,
512                                           FIND_CHILD )) )
513     {
514         vlc_object_detach( p_playlist );
515         vlc_object_release( p_playlist );
516         playlist_Destroy( p_playlist );
517     }
518
519     /*
520      * Free video outputs
521      */
522     msg_Dbg( p_intf, "removing all video outputs" );
523     while( (p_vout = vlc_object_find( p_intf->p_vlc, 
524                                       VLC_OBJECT_VOUT, FIND_CHILD )) )
525     {
526         vlc_object_detach( p_vout );
527         vlc_object_release( p_vout );
528         vout_Destroy( p_vout );
529     }
530
531     [o_slider_lock release];
532
533     if( o_prefs != nil )
534     {
535         [o_prefs release];
536         o_prefs = nil;
537     }
538
539     [NSApp stop: nil];
540
541     /* write cached user defaults to disk */
542     [[NSUserDefaults standardUserDefaults] synchronize];
543
544     /* send a dummy event to break out of the event loop */
545     pEvent = [NSEvent mouseEventWithType: NSLeftMouseDown
546                 location: NSMakePoint( 1, 1 ) modifierFlags: 0
547                 timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
548                 context: [NSGraphicsContext currentContext] eventNumber: 1
549                 clickCount: 1 pressure: 0.0];
550     [NSApp postEvent: pEvent atStart: YES];
551 }
552
553 - (void)manageMode
554 {
555     vlc_bool_t b_input;
556     vlc_bool_t b_plmul = 0;
557     vlc_bool_t b_control = 0;
558     playlist_t * p_playlist = NULL;
559     intf_thread_t * p_intf = [NSApp getIntf];
560
561     if( ( b_input = ( p_intf->p_sys->p_input != NULL ) ) )
562     {
563         /* control buttons for free pace streams */
564         b_control = p_intf->p_sys->p_input->stream.b_pace_control;
565
566         /* get ready for menu regeneration */
567         p_intf->p_sys->b_program_update = 1;
568         p_intf->p_sys->b_title_update = 1;
569         p_intf->p_sys->b_chapter_update = 1;
570         p_intf->p_sys->b_audio_update = 1;
571         p_intf->p_sys->b_spu_update = 1;
572         p_intf->p_sys->i_part = 0;
573
574         p_intf->p_sys->p_input->stream.b_changed = 0;
575         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
576     }
577     else
578     {
579         /* unsensitize menus */
580         [o_mi_program setEnabled: FALSE];
581         [o_mi_title setEnabled: FALSE];
582         [o_mi_chapter setEnabled: FALSE];
583         [o_mi_language setEnabled: FALSE];
584         [o_mi_subtitle setEnabled: FALSE];
585         [o_mi_channels setEnabled: FALSE];
586         [o_mi_device setEnabled: FALSE];
587         [o_mi_screen setEnabled: FALSE];
588         [o_mi_close_window setEnabled: FALSE];
589     }
590
591     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, 
592                                               FIND_ANYWHERE ); 
593     if( p_playlist != NULL )
594     {
595         vlc_mutex_lock( &p_playlist->object_lock );
596         b_plmul = p_playlist->i_size > 1;
597         vlc_mutex_unlock( &p_playlist->object_lock );
598         vlc_object_release( p_playlist );
599     }
600
601     /* set control items */
602     [o_btn_stop setEnabled: b_input];
603     [o_btn_pause setEnabled: b_control];
604     [o_btn_faster setEnabled: b_control];
605     [o_btn_slower setEnabled: b_control];
606     [o_btn_prev setEnabled: b_plmul];
607     [o_btn_next setEnabled: b_plmul];
608
609     if ( (p_intf->p_sys->b_loop = config_GetInt( p_intf, "loop" )) )
610     {
611         [o_mi_loop setState: NSOnState];
612     }
613     else
614     {
615         [o_mi_loop setState: NSOffState];
616     }
617 }
618
619 - (void)setupMenus
620 {
621     unsigned int i, i_nb_items;
622     NSMenuItem * o_item;
623     NSString * o_menu_title;
624     char psz_title[ 256 ];
625
626     es_descriptor_t * p_audio_es = NULL;
627     es_descriptor_t * p_spu_es = NULL;
628
629     intf_thread_t * p_intf = [NSApp getIntf];
630
631     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
632     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
633                                      p_intf->p_sys->b_program_update;
634     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
635                                    p_intf->p_sys->b_program_update;
636
637 #define p_input (p_intf->p_sys->p_input)
638
639     if( p_intf->p_sys->b_program_update )
640     {
641         NSMenu * o_program;
642         SEL pf_toggle_program;
643         pgrm_descriptor_t * p_pgrm;
644
645         if( p_input->stream.p_new_program )
646         {
647             p_pgrm = p_input->stream.p_new_program;
648         }
649         else
650         {
651             p_pgrm = p_input->stream.p_selected_program;
652         }
653
654         o_program = [o_mi_program submenu];
655         pf_toggle_program = @selector(toggleProgram:);
656
657         /* remove previous program items */
658         i_nb_items = [o_program numberOfItems];
659         for( i = 0; i < i_nb_items; i++ )
660         {
661             [o_program removeItemAtIndex: 0];
662         }
663
664         /* make (un)sensitive */
665         [o_mi_program setEnabled: 
666             p_input->stream.i_pgrm_number > 1];
667
668         /* add program items */
669         for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
670         {
671             snprintf( psz_title, sizeof(psz_title), "id %d",
672                 p_input->stream.pp_programs[i]->i_number );
673             psz_title[sizeof(psz_title) - 1] = '\0';
674
675             o_menu_title = [NSString stringWithCString: psz_title];
676
677             o_item = [o_program addItemWithTitle: o_menu_title
678                 action: pf_toggle_program keyEquivalent: @""];
679             [o_item setTag: p_input->stream.pp_programs[i]->i_number];
680             [o_item setTarget: o_controls];
681
682             if( p_pgrm == p_input->stream.pp_programs[i] )
683             {
684                 [o_item setState: NSOnState];
685             }
686         }
687
688         p_intf->p_sys->b_program_update = 0;
689     }
690
691     if( p_intf->p_sys->b_title_update )
692     {
693         NSMenu * o_title;
694         SEL pf_toggle_title;
695
696         o_title = [o_mi_title submenu];
697         pf_toggle_title = @selector(toggleTitle:);
698
699         /* remove previous title items */
700         i_nb_items = [o_title numberOfItems];
701         for( i = 0; i < i_nb_items; i++ )
702         {
703             [o_title removeItemAtIndex: 0];
704         }
705
706         /* make (un)sensitive */
707         [o_mi_title setEnabled: 
708             p_input->stream.i_area_nb > 1];
709
710         /* add title items */
711         for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
712         {
713             snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
714                 p_input->stream.pp_areas[i]->i_part_nb );
715             psz_title[sizeof(psz_title) - 1] = '\0';
716
717             o_menu_title = [NSString stringWithCString: psz_title];
718
719             o_item = [o_title addItemWithTitle: o_menu_title
720                 action: pf_toggle_title keyEquivalent: @""];
721             [o_item setTag: i];
722             [o_item setTarget: o_controls];
723
724             if( ( p_input->stream.pp_areas[i] ==
725                 p_input->stream.p_selected_area ) )
726             {
727                 [o_item setState: NSOnState];
728             }
729         }
730
731         p_intf->p_sys->b_title_update = 0;
732     }
733
734     if( p_intf->p_sys->b_chapter_update )
735     {
736         NSMenu * o_chapter;
737         SEL pf_toggle_chapter;
738
739         o_chapter = [o_mi_chapter submenu];
740         pf_toggle_chapter = @selector(toggleChapter:);
741
742         /* remove previous chapter items */
743         i_nb_items = [o_chapter numberOfItems];
744         for( i = 0; i < i_nb_items; i++ )
745         {
746             [o_chapter removeItemAtIndex: 0];
747         }
748
749         /* make (un)sensitive */
750         [o_mi_chapter setEnabled: 
751             p_input->stream.p_selected_area->i_part_nb > 1];
752
753         /* add chapter items */
754         for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
755         {
756             snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
757             psz_title[sizeof(psz_title) - 1] = '\0';
758
759             o_menu_title = [NSString stringWithCString: psz_title];
760
761             o_item = [o_chapter addItemWithTitle: o_menu_title
762                 action: pf_toggle_chapter keyEquivalent: @""];
763             [o_item setTag: i + 1];
764             [o_item setTarget: o_controls];
765
766             if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
767             {
768                 [o_item setState: NSOnState];
769             }
770         }
771
772         p_intf->p_sys->i_part =
773                 p_input->stream.p_selected_area->i_part;
774
775         p_intf->p_sys->b_chapter_update = 0;
776     }
777
778     for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
779     {
780         if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
781         {
782             p_audio_es = p_input->stream.pp_selected_es[i];
783         }
784         else if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
785         {
786             p_spu_es = p_input->stream.pp_selected_es[i];
787         }
788     }
789
790     vlc_mutex_unlock( &p_input->stream.stream_lock );
791
792     if( p_intf->p_sys->b_audio_update )
793     {
794         [self setupLangMenu: o_mi_language es: p_audio_es
795             category: AUDIO_ES selector: @selector(toggleLanguage:)];
796
797         p_intf->p_sys->b_audio_update = 0;
798     }
799
800     if( p_intf->p_sys->b_spu_update )
801     {
802         [self setupLangMenu: o_mi_subtitle es: p_spu_es
803             category: SPU_ES selector: @selector(toggleLanguage:)];
804
805         p_intf->p_sys->b_spu_update = 0;
806     }
807
808     if ( p_intf->p_sys->b_aout_update )
809     {
810         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
811                                                     FIND_ANYWHERE );
812
813         if ( p_aout != NULL )
814         {
815             vlc_value_t val;
816             val.b_bool = 0;
817
818             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
819
820             [self setupVarMenu: o_mi_channels target: (vlc_object_t *)p_aout
821                 var: "audio-channels" selector: @selector(toggleVar:)];
822
823             [self setupVarMenu: o_mi_device target: (vlc_object_t *)p_aout
824                 var: "audio-device" selector: @selector(toggleVar:)];
825
826             vlc_object_release( (vlc_object_t *)p_aout );
827         }
828
829         p_intf->p_sys->b_aout_update = 0;
830     }
831
832     if ( p_intf->p_sys->b_vout_update )
833     {
834         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
835                                                   FIND_ANYWHERE );
836
837         if ( p_vout != NULL )
838         {
839             vlc_value_t val;
840             val.b_bool = 0;
841
842             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
843
844             [self setupVarMenu: o_mi_screen target: (vlc_object_t *)p_vout
845                 var: "video-device" selector: @selector(toggleVar:)];
846
847             vlc_object_release( (vlc_object_t *)p_vout );
848
849             [o_mi_close_window setEnabled: TRUE];
850         }
851
852         p_intf->p_sys->b_vout_update = 0;
853     }
854
855     vlc_mutex_lock( &p_input->stream.stream_lock );
856
857 #undef p_input
858 }
859
860 - (void)setupLangMenu:(NSMenuItem *)o_mi
861                       es:(es_descriptor_t *)p_es
862                       category:(int)i_cat
863                       selector:(SEL)pf_callback
864 {
865     unsigned int i, i_nb_items;
866     NSMenu * o_menu = [o_mi submenu];
867     intf_thread_t * p_intf = [NSApp getIntf];
868
869     /* remove previous language items */
870     i_nb_items = [o_menu numberOfItems];
871     for( i = 0; i < i_nb_items; i++ )
872     {
873         [o_menu removeItemAtIndex: 0];
874     }
875
876     /* make sensitive : we can't change it after we build the menu, and
877      * before, we don't yet how many items we will have. So make it
878      * always sensitive. --Meuuh */
879     [o_mi setEnabled: TRUE];
880
881     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
882
883 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
884     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
885     {
886         if( ( ES->i_cat == i_cat ) &&
887             ( !ES->p_pgrm ||
888               ES->p_pgrm ==
889                  p_intf->p_sys->p_input->stream.p_selected_program ) )
890         {
891             NSMenuItem * o_lmi;
892             NSString * o_title;
893
894             if( *ES->psz_desc )
895             {
896                 o_title = [NSString stringWithCString: ES->psz_desc];
897             }
898             else
899             {
900                 char psz_title[ 256 ];
901
902                 snprintf( psz_title, sizeof(psz_title), "Language 0x%x",
903                           ES->i_id );
904                 psz_title[sizeof(psz_title) - 1] = '\0';
905
906                 o_title = [NSString stringWithCString: psz_title];
907             }
908
909             o_lmi = [o_menu addItemWithTitle: o_title
910                 action: pf_callback keyEquivalent: @""];
911             [o_lmi setRepresentedObject: 
912                 [NSValue valueWithPointer: ES]];
913             [o_lmi setTarget: o_controls];
914             [o_lmi setTag: i_cat];
915
916             if( /*p_es == ES*/ ES->p_decoder_fifo != NULL )
917             {
918                 [o_lmi setState: NSOnState];
919             }
920         }
921     }
922 #undef ES
923
924     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
925 }
926
927 - (void)setupVarMenu:(NSMenuItem *)o_mi
928                      target:(vlc_object_t *)p_object
929                      var:(const char *)psz_variable
930                      selector:(SEL)pf_callback
931 {
932     int i, i_nb_items;
933     NSMenu * o_menu = [o_mi submenu];
934     vlc_value_t val;
935     char * psz_value;
936
937     /* remove previous items */
938     i_nb_items = [o_menu numberOfItems];
939     for( i = 0; i < i_nb_items; i++ )
940     {
941         [o_menu removeItemAtIndex: 0];
942     }
943
944     if ( var_Get( p_object, psz_variable, &val ) < 0 )
945     {
946         return;
947     }
948     psz_value = val.psz_string;
949
950     if ( var_Change( p_object, psz_variable,
951                      VLC_VAR_GETLIST, &val ) < 0 )
952     {
953         free( psz_value );
954         return;
955     }
956
957     /* make (un)sensitive */
958     [o_mi setEnabled: (val.p_list->i_count > 0)];
959
960     for ( i = 0; i < val.p_list->i_count; i++ )
961     {
962         NSMenuItem * o_lmi;
963         NSString * o_title;
964
965         o_title = [NSString stringWithCString: val.p_list->p_values[i].psz_string];
966         o_lmi = [o_menu addItemWithTitle: o_title
967                  action: pf_callback keyEquivalent: @""];
968         /* FIXME: this isn't 64-bit clean ! */
969         [o_lmi setTag: (int)psz_variable];
970         [o_lmi setRepresentedObject:
971             [NSValue valueWithPointer: p_object]];
972         [o_lmi setTarget: o_controls];
973
974         if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
975             [o_lmi setState: NSOnState];
976     }
977
978     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
979                 &val );
980
981     free( psz_value );
982 }
983
984 - (IBAction)clearRecentItems:(id)sender
985 {
986     [[NSDocumentController sharedDocumentController]
987                           clearRecentDocuments: nil];
988 }
989
990 - (void)openRecentItem:(id)sender
991 {
992     [self application: nil openFile: [sender title]]; 
993 }
994
995 - (IBAction)viewPreferences:(id)sender
996 {
997     if( o_prefs == nil )
998     {
999         o_prefs = [[VLCPrefs alloc] init];
1000     }
1001
1002     [o_prefs createPrefPanel: @"main"];
1003 }
1004
1005 - (IBAction)timesliderUpdate:(id)sender
1006 {
1007     switch( [[NSApp currentEvent] type] )
1008     {
1009         case NSLeftMouseDown:
1010             [o_slider_lock lock];
1011             break;
1012
1013         case NSLeftMouseUp:
1014             f_slider = [sender floatValue];
1015             [o_slider_lock unlock];
1016             break;
1017
1018         default:
1019             break;
1020     }
1021 }
1022
1023 - (void)displayTime
1024 {
1025     intf_thread_t * p_intf = [NSApp getIntf];
1026     input_thread_t * p_input = p_intf->p_sys->p_input;
1027
1028     if( p_input == NULL )
1029     {
1030         [o_timeslider setEnabled: FALSE];
1031         [o_timeslider setFloatValue: 0.0];
1032         [o_timefield setStringValue: @"0:00:00"]; 
1033
1034         return;
1035     }
1036
1037 #define p_area p_input->stream.p_selected_area
1038
1039     if( p_input->stream.b_changed )
1040     {
1041         [o_timeslider setEnabled: p_input->stream.b_seekable];
1042     }
1043     else if( p_intf->p_sys->b_playing )
1044     {
1045         NSString * o_time;
1046         char psz_time[ OFFSETTOTIME_MAX_SIZE ];
1047
1048         input_OffsetToTime( p_input, psz_time, p_area->i_tell );
1049
1050         o_time = [NSString stringWithCString: psz_time];
1051         [o_timefield setStringValue: o_time];
1052
1053         if( p_input->stream.b_seekable )
1054         {
1055             if( f_slider == f_slider_old )
1056             {
1057                 float f_updated = ( 100. * p_area->i_tell ) /
1058                                            p_area->i_size;
1059
1060                 if( f_slider != f_updated && [o_slider_lock tryLock] )
1061                 {
1062                     [o_timeslider setFloatValue: f_updated];
1063                     [o_slider_lock unlock]; 
1064                 }
1065             }
1066             else
1067             {
1068                 off_t i_seek = ( f_slider * p_area->i_size ) / 100;
1069
1070                 /* release the lock to be able to seek */
1071                 vlc_mutex_unlock( &p_input->stream.stream_lock );
1072                 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
1073                 vlc_mutex_lock( &p_input->stream.stream_lock ); 
1074
1075                 /* Update the old value */
1076                 f_slider_old = f_slider;
1077             }
1078         }
1079     }
1080 #undef p_area
1081 }
1082
1083 - (IBAction)closeError:(id)sender
1084 {
1085     /* Error panel */
1086     [o_err_msg setEditable: YES];
1087     [o_err_msg setSelectedRange:
1088                 NSMakeRange( 0, [[o_err_msg string] length] )];
1089     [o_err_msg insertText: @""];
1090     [o_err_msg setEditable: NO];
1091     [o_error performClose: self];
1092 }
1093
1094 @end
1095
1096 @implementation VLCMain (NSMenuValidation)
1097
1098 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1099 {
1100     BOOL bEnabled = TRUE;
1101
1102     /* Recent Items Menu */
1103
1104     if( [[o_mi title] isEqualToString: _NS("Clear Menu")] )
1105     {
1106         NSMenu * o_menu = [o_mi_open_recent submenu];
1107         int i_nb_items = [o_menu numberOfItems];
1108         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1109                                                        recentDocumentURLs];
1110         UInt32 i_nb_docs = [o_docs count];
1111
1112         if( i_nb_items > 1 )
1113         {
1114             while( --i_nb_items )
1115             {
1116                 [o_menu removeItemAtIndex: 0];
1117             }
1118         }
1119
1120         if( i_nb_docs > 0 )
1121         {
1122             NSURL * o_url;
1123             NSString * o_doc;
1124
1125             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1126
1127             while( TRUE )
1128             {
1129                 i_nb_docs--;
1130
1131                 o_url = [o_docs objectAtIndex: i_nb_docs];
1132
1133                 if( [o_url isFileURL] )
1134                 {
1135                     o_doc = [o_url path];
1136                 }
1137                 else
1138                 {
1139                     o_doc = [o_url absoluteString];
1140                 }
1141
1142                 [o_menu insertItemWithTitle: o_doc
1143                     action: @selector(openRecentItem:)
1144                     keyEquivalent: @"" atIndex: 0]; 
1145
1146                 if( i_nb_docs == 0 )
1147                 {
1148                     break;
1149                 }
1150             } 
1151         }
1152         else
1153         {
1154             bEnabled = FALSE;
1155         }
1156     }
1157
1158     return( bEnabled );
1159 }
1160
1161 @end
1162
1163 @implementation VLCMain (Internal)
1164
1165 - (void)handlePortMessage:(NSPortMessage *)o_msg
1166 {
1167     NSData * o_data;
1168     NSValue * o_value;
1169     NSInvocation * o_inv;
1170     vout_thread_t * p_vout;
1171  
1172     o_data = [[o_msg components] lastObject];
1173     o_inv = *((NSInvocation **)[o_data bytes]); 
1174     [o_inv getArgument: &o_value atIndex: 2];
1175     p_vout = (vout_thread_t *)[o_value pointerValue];
1176
1177     [p_vout->p_sys->o_lock lock];
1178     [o_inv invoke];
1179     [p_vout->p_sys->o_lock unlockWithCondition: 1];
1180 }
1181
1182 @end