1c0df640c9fee50b162a13c62884583dc40167f3
[vlc.git] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: intf.m,v 1.9 2002/12/08 23:38:02 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 #include "asystm.h"
39
40 /*****************************************************************************
41  * Local prototypes.
42  *****************************************************************************/
43 static void Run       ( intf_thread_t *p_intf );
44
45 /*****************************************************************************
46  * OpenIntf: initialize interface
47  *****************************************************************************/
48 int E_(OpenIntf) ( vlc_object_t *p_this )
49 {   
50     intf_thread_t *p_intf = (intf_thread_t*) p_this;
51
52     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
53     if( p_intf->p_sys == NULL )
54     {
55         return( 1 );
56     }
57
58     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
59
60     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
61     p_intf->p_sys->o_sendport = [[NSPort port] retain];
62
63     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
64
65     p_intf->pf_run = Run;
66
67     [[VLCApplication sharedApplication] autorelease];
68     [NSApp initIntlSupport];
69     [NSApp setIntf: p_intf];
70
71     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
72
73     return( 0 );
74 }
75
76 /*****************************************************************************
77  * CloseIntf: destroy interface
78  *****************************************************************************/
79 void E_(CloseIntf) ( vlc_object_t *p_this )
80 {
81     intf_thread_t *p_intf = (intf_thread_t*) p_this;
82
83     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
84
85     [p_intf->p_sys->o_sendport release];
86     [p_intf->p_sys->o_pool release];
87
88     free( p_intf->p_sys );
89 }
90
91 /*****************************************************************************
92  * Run: main loop
93  *****************************************************************************/
94 static void Run( intf_thread_t *p_intf )
95 {
96     [NSApp run];
97 }
98
99 /*****************************************************************************
100  * VLCApplication implementation 
101  *****************************************************************************/
102 @implementation VLCApplication
103
104 - (id)init
105 {
106     /* default encoding: ISO-8859-1 */
107     i_encoding = NSISOLatin1StringEncoding;
108
109     return( [super init] );
110 }
111
112 - (void)initIntlSupport
113 {
114     char *psz_lang = getenv( "LANG" );
115
116     if( psz_lang == NULL )
117     {
118         return;
119     }
120
121     if( strncmp( psz_lang, "pl", 2 ) == 0 )
122     {
123         i_encoding = NSISOLatin2StringEncoding;
124     }
125     else if( strncmp( psz_lang, "ja", 2 ) == 0 ) 
126     {
127         i_encoding = NSJapaneseEUCStringEncoding;
128     }
129     else if( strncmp( psz_lang, "ru", 2 ) == 0 )
130     {
131 #define CFSENC2NSSENC(e) CFStringConvertEncodingToNSStringEncoding(e)
132         i_encoding = CFSENC2NSSENC( kCFStringEncodingKOI8_R ); 
133 #undef CFSENC2NSSENC
134     }
135 }
136
137 - (NSString *)localizedString:(char *)psz
138 {
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     [o_msgs_panel setTitle: _NS("Messages")];
188     [o_msgs_btn_ok setTitle: _NS("Close")];
189
190     [o_mi_about setTitle: _NS("About vlc")];
191     [o_mi_prefs setTitle: _NS("Preferences")];
192     [o_mi_hide setTitle: _NS("Hide vlc")];
193     [o_mi_hide_others setTitle: _NS("Hide Others")];
194     [o_mi_show_all setTitle: _NS("Show All")];
195     [o_mi_quit setTitle: _NS("Quit vlc")];
196
197     [o_mu_file setTitle: _NS("File")];
198     [o_mi_open_file setTitle: _NS("Open File...")];
199     [o_mi_open_generic setTitle: _NS("Open Generic...")];
200     [o_mi_open_disc setTitle: _NS("Open Disc...")];
201     [o_mi_open_net setTitle: _NS("Open Network...")];
202     [o_mi_open_recent setTitle: _NS("Open Recent")];
203     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
204
205     [o_mu_edit setTitle: _NS("Edit")];
206     [o_mi_cut setTitle: _NS("Cut")];
207     [o_mi_copy setTitle: _NS("Copy")];
208     [o_mi_paste setTitle: _NS("Paste")];
209     [o_mi_clear setTitle: _NS("Clear")];
210     [o_mi_select_all setTitle: _NS("Select All")];
211
212     [o_mu_view setTitle: _NS("View")];
213     [o_mi_playlist setTitle: _NS("Playlist")];
214     [o_mi_messages setTitle: _NS("Messages")];
215
216     [o_mu_controls setTitle: _NS("Controls")];
217     [o_mi_play setTitle: _NS("Play")];
218     [o_mi_pause setTitle: _NS("Pause")];
219     [o_mi_stop setTitle: _NS("Stop")];
220     [o_mi_faster setTitle: _NS("Faster")];
221     [o_mi_slower setTitle: _NS("Slower")];
222     [o_mi_previous setTitle: _NS("Prev")];
223     [o_mi_next setTitle: _NS("Next")];
224     [o_mi_loop setTitle: _NS("Loop")];
225     [o_mi_vol_up setTitle: _NS("Louder")];
226     [o_mi_vol_down setTitle: _NS("Softer")];
227     [o_mi_mute setTitle: _NS("Mute")];
228     [o_mi_channels setTitle: _NS("Channels")];
229     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
230     [o_mi_screen setTitle: _NS("Screen")];
231     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
232     [o_mi_program setTitle: _NS("Program")];
233     [o_mi_title setTitle: _NS("Title")];
234     [o_mi_chapter setTitle: _NS("Chapter")];
235     [o_mi_language setTitle: _NS("Language")];
236     [o_mi_subtitle setTitle: _NS("Subtitles")];
237
238     [o_mu_window setTitle: _NS("Window")];
239     [o_mi_minimize setTitle: _NS("Minimize")];
240     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
241
242     /* dock menu */
243     [o_dmi_play setTitle: _NS("Play")];
244     [o_dmi_pause setTitle: _NS("Pause")];
245     [o_dmi_stop setTitle: _NS("Stop")];
246
247     [self manageMode];
248 }
249
250 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
251 {
252     intf_thread_t * p_intf = [NSApp getIntf];
253
254     [NSThread detachNewThreadSelector: @selector(manage)
255         toTarget: self withObject: nil];
256
257     [p_intf->p_sys->o_sendport setDelegate: self];
258     [[NSRunLoop currentRunLoop] 
259         addPort: p_intf->p_sys->o_sendport
260         forMode: NSDefaultRunLoopMode];
261
262     // Since we need the sound menu now, it's a good time to create the sound system class
263     asystm=[[MacOSXAudioSystem alloc] initWithGUI:self];
264     [asystm retain];
265     
266 }
267
268 - (void)noopAction:(id)sender {
269     // nothing
270 }
271
272 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
273 {
274     [o_playlist appendArray:
275         [NSArray arrayWithObject: o_filename] atPos: -1];
276
277     return( TRUE );
278 }
279
280 - (void)manage
281 {
282     NSDate * o_sleep_date;
283     intf_thread_t * p_intf = [NSApp getIntf];
284     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
285
286     while( !p_intf->b_die )
287     {
288         int i_start, i_stop;
289
290         vlc_mutex_lock( &p_intf->change_lock );
291
292         /* update the input */
293         if( p_intf->p_sys->p_input == NULL )
294         {
295             p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
296                                                               FIND_ANYWHERE );
297         }
298         else if( p_intf->p_sys->p_input->b_dead )
299         {
300             vlc_object_release( p_intf->p_sys->p_input );
301             p_intf->p_sys->p_input = NULL;
302         }
303
304         if( p_intf->p_sys->p_input != NULL )
305         {
306             vlc_bool_t b_need_menus = 0;
307             input_thread_t * p_input = p_intf->p_sys->p_input;
308             aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
309                                                         FIND_ANYWHERE );
310             vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
311                                                       FIND_ANYWHERE );
312
313             vlc_mutex_lock( &p_input->stream.stream_lock );
314
315             if( !p_input->b_die )
316             {
317                 /* New input or stream map change */
318                 if( p_input->stream.b_changed )
319                 {
320                     [self manageMode];
321                     b_need_menus = 1;
322                     p_intf->p_sys->b_playing = 1;
323                 }
324
325                 if( p_intf->p_sys->i_part !=
326                     p_input->stream.p_selected_area->i_part )
327                 {
328                     p_intf->p_sys->b_chapter_update = 1;
329                     b_need_menus = 1;
330                 }
331             }
332
333             if ( p_aout != NULL )
334             {
335                 vlc_value_t val;
336                 if ( var_Get( (vlc_object_t *)p_aout, "intf-change", &val )
337                       >= 0 && val.b_bool )
338                 {
339                     p_intf->p_sys->b_aout_update = 1;
340                     b_need_menus = 1;
341                 }
342                 vlc_object_release( (vlc_object_t *)p_aout );
343             }
344
345             if ( p_vout != NULL )
346             {
347                 vlc_value_t val;
348                 if ( var_Get( (vlc_object_t *)p_vout, "intf-change", &val )
349                       >= 0 && val.b_bool )
350                 {
351                     p_intf->p_sys->b_vout_update = 1;
352                     b_need_menus = 1;
353                 }
354                 vlc_object_release( (vlc_object_t *)p_vout );
355             }
356
357             if ( b_need_menus )
358                 [self setupMenus];
359
360             vlc_mutex_unlock( &p_input->stream.stream_lock );
361         }
362         else if( p_intf->p_sys->b_playing && !p_intf->b_die )
363         {
364             [self manageMode];
365             p_intf->p_sys->b_playing = 0;
366         }
367
368         /* update the log window */
369         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
370         i_stop = *p_intf->p_sys->p_sub->pi_stop;
371         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
372
373         if( p_intf->p_sys->p_sub->i_start != i_stop )
374         {
375             NSColor *o_white = [NSColor whiteColor];
376             NSColor *o_red = [NSColor redColor];
377             NSColor *o_yellow = [NSColor yellowColor];
378             NSColor *o_gray = [NSColor grayColor];
379
380             unsigned int ui_length = [[o_messages string] length];
381
382             NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
383             static const char * ppsz_type[4] = { ": ", " error: ", 
384                                                  " warning: ", " debug: " }; 
385         
386             [o_messages setEditable: YES];
387             [o_messages setSelectedRange: NSMakeRange( ui_length, 0 )];
388             [o_messages scrollRangeToVisible: NSMakeRange( ui_length, 0 )];
389
390             for( i_start = p_intf->p_sys->p_sub->i_start;
391                  i_start != i_stop;
392                  i_start = (i_start+1) % VLC_MSG_QSIZE )
393             {
394                 NSString *o_msg;
395                 NSDictionary *o_attr;
396                 NSAttributedString *o_msg_color;
397                 int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
398
399                 o_attr = [NSDictionary dictionaryWithObject: o_gray
400                     forKey: NSForegroundColorAttributeName];
401                 o_msg = [NSString stringWithFormat: @"%s%s",
402                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module, 
403                     ppsz_type[i_type]];
404                 o_msg_color = [[NSAttributedString alloc]
405                     initWithString: o_msg attributes: o_attr];
406                 [o_messages insertText: o_msg_color];
407
408                 o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
409                     forKey: NSForegroundColorAttributeName];
410                 o_msg = [NSString stringWithCString:
411                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
412                 o_msg_color = [[NSAttributedString alloc]
413                     initWithString: o_msg attributes: o_attr];
414                 [o_messages insertText: o_msg_color];
415
416                 [o_messages insertText: @"\n"];
417             }
418
419             [o_messages setEditable: NO];
420
421             vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
422             p_intf->p_sys->p_sub->i_start = i_start;
423             vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
424         }
425
426         vlc_mutex_unlock( &p_intf->change_lock );
427
428         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: 0.1];
429         [NSThread sleepUntilDate: o_sleep_date];
430     }
431
432     [self terminate];
433
434     [o_pool release];
435 }
436
437 - (void)terminate
438 {
439     NSEvent * pEvent;
440     vout_thread_t * p_vout;
441     playlist_t * p_playlist;
442     intf_thread_t * p_intf = [NSApp getIntf];
443
444     /* release input */
445     if( p_intf->p_sys->p_input )
446     {
447         vlc_object_release( p_intf->p_sys->p_input );
448         p_intf->p_sys->p_input = NULL;
449     }
450
451     /*
452      * Free playlists
453      */
454     msg_Dbg( p_intf, "removing all playlists" );
455     while( (p_playlist = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_PLAYLIST,
456                                           FIND_CHILD )) )
457     {
458         vlc_object_detach( p_playlist );
459         vlc_object_release( p_playlist );
460         playlist_Destroy( p_playlist );
461     }
462
463     /*
464      * Free video outputs
465      */
466     msg_Dbg( p_intf, "removing all video outputs" );
467     while( (p_vout = vlc_object_find( p_intf->p_vlc, 
468                                       VLC_OBJECT_VOUT, FIND_CHILD )) )
469     {
470         vlc_object_detach( p_vout );
471         vlc_object_release( p_vout );
472         vout_Destroy( p_vout );
473     }
474
475     if( o_prefs != nil )
476     {
477         [o_prefs release];
478         o_prefs = nil;
479     }
480
481     [NSApp stop: nil];
482
483     /* write cached user defaults to disk */
484     [[NSUserDefaults standardUserDefaults] synchronize];
485
486     /* send a dummy event to break out of the event loop */
487     pEvent = [NSEvent mouseEventWithType: NSLeftMouseDown
488                 location: NSMakePoint( 1, 1 ) modifierFlags: 0
489                 timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
490                 context: [NSGraphicsContext currentContext] eventNumber: 1
491                 clickCount: 1 pressure: 0.0];
492     [NSApp postEvent: pEvent atStart: YES];
493
494     [asystm release];
495 }
496
497 - (void)manageMode
498 {
499     vlc_bool_t b_control = 0;
500     intf_thread_t * p_intf = [NSApp getIntf];
501
502     if( p_intf->p_sys->p_input != NULL )
503     {
504         /* control buttons for free pace streams */
505         b_control = p_intf->p_sys->p_input->stream.b_pace_control;
506
507         /* get ready for menu regeneration */
508         p_intf->p_sys->b_program_update = 1;
509         p_intf->p_sys->b_title_update = 1;
510         p_intf->p_sys->b_chapter_update = 1;
511         p_intf->p_sys->b_audio_update = 1;
512         p_intf->p_sys->b_spu_update = 1;
513         p_intf->p_sys->i_part = 0;
514
515         p_intf->p_sys->p_input->stream.b_changed = 0;
516         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
517     }
518     else
519     {
520         /* unsensitize menus */
521         [o_mi_program setEnabled: FALSE];
522         [o_mi_title setEnabled: FALSE];
523         [o_mi_chapter setEnabled: FALSE];
524         [o_mi_language setEnabled: FALSE];
525         [o_mi_subtitle setEnabled: FALSE];
526         [o_mi_channels setEnabled: FALSE];
527         [o_mi_screen setEnabled: FALSE];
528     }
529 }
530
531 - (void)setupMenus
532 {
533     int i, i_nb_items;
534     NSMenuItem * o_item;
535     NSString * o_menu_title;
536     char psz_title[ 256 ];
537
538     es_descriptor_t * p_audio_es = NULL;
539     es_descriptor_t * p_spu_es = NULL;
540
541     intf_thread_t * p_intf = [NSApp getIntf];
542
543     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
544     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
545                                      p_intf->p_sys->b_program_update;
546     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
547                                    p_intf->p_sys->b_program_update;
548
549 #define p_input (p_intf->p_sys->p_input)
550
551     if( p_intf->p_sys->b_program_update )
552     {
553         NSMenu * o_program;
554         SEL pf_toggle_program;
555         pgrm_descriptor_t * p_pgrm;
556
557         if( p_input->stream.p_new_program )
558         {
559             p_pgrm = p_input->stream.p_new_program;
560         }
561         else
562         {
563             p_pgrm = p_input->stream.p_selected_program;
564         }
565
566         o_program = [o_mi_program submenu];
567         pf_toggle_program = @selector(toggleProgram:);
568
569         /* remove previous program items */
570         i_nb_items = [o_program numberOfItems];
571         for( i = 0; i < i_nb_items; i++ )
572         {
573             [o_program removeItemAtIndex: 0];
574         }
575
576         /* make (un)sensitive */
577         [o_mi_program setEnabled: 
578             p_input->stream.i_pgrm_number > 1];
579
580         /* add program items */
581         for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
582         {
583             snprintf( psz_title, sizeof(psz_title), "id %d",
584                 p_input->stream.pp_programs[i]->i_number );
585             psz_title[sizeof(psz_title) - 1] = '\0';
586
587             o_menu_title = [NSString stringWithCString: psz_title];
588
589             o_item = [o_program addItemWithTitle: o_menu_title
590                 action: pf_toggle_program keyEquivalent: @""];
591             [o_item setTag: p_input->stream.pp_programs[i]->i_number];
592             [o_item setTarget: o_controls];
593
594             if( p_pgrm == p_input->stream.pp_programs[i] )
595             {
596                 [o_item setState: NSOnState];
597             }
598         }
599
600         p_intf->p_sys->b_program_update = 0;
601     }
602
603     if( p_intf->p_sys->b_title_update )
604     {
605         NSMenu * o_title;
606         SEL pf_toggle_title;
607
608         o_title = [o_mi_title submenu];
609         pf_toggle_title = @selector(toggleTitle:);
610
611         /* remove previous title items */
612         i_nb_items = [o_title numberOfItems];
613         for( i = 0; i < i_nb_items; i++ )
614         {
615             [o_title removeItemAtIndex: 0];
616         }
617
618         /* make (un)sensitive */
619         [o_mi_title setEnabled: 
620             p_input->stream.i_area_nb > 1];
621
622         /* add title items */
623         for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
624         {
625             snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
626                 p_input->stream.pp_areas[i]->i_part_nb );
627             psz_title[sizeof(psz_title) - 1] = '\0';
628
629             o_menu_title = [NSString stringWithCString: psz_title];
630
631             o_item = [o_title addItemWithTitle: o_menu_title
632                 action: pf_toggle_title keyEquivalent: @""];
633             [o_item setTag: i];
634             [o_item setTarget: o_controls];
635
636             if( ( p_input->stream.pp_areas[i] ==
637                 p_input->stream.p_selected_area ) )
638             {
639                 [o_item setState: NSOnState];
640             }
641         }
642
643         p_intf->p_sys->b_title_update = 0;
644     }
645
646     if( p_intf->p_sys->b_chapter_update )
647     {
648         NSMenu * o_chapter;
649         SEL pf_toggle_chapter;
650
651         o_chapter = [o_mi_chapter submenu];
652         pf_toggle_chapter = @selector(toggleChapter:);
653
654         /* remove previous chapter items */
655         i_nb_items = [o_chapter numberOfItems];
656         for( i = 0; i < i_nb_items; i++ )
657         {
658             [o_chapter removeItemAtIndex: 0];
659         }
660
661         /* make (un)sensitive */
662         [o_mi_chapter setEnabled: 
663             p_input->stream.p_selected_area->i_part_nb > 1];
664
665         /* add chapter items */
666         for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
667         {
668             snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
669             psz_title[sizeof(psz_title) - 1] = '\0';
670
671             o_menu_title = [NSString stringWithCString: psz_title];
672
673             o_item = [o_chapter addItemWithTitle: o_menu_title
674                 action: pf_toggle_chapter keyEquivalent: @""];
675             [o_item setTag: i + 1];
676             [o_item setTarget: o_controls];
677
678             if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
679             {
680                 [o_item setState: NSOnState];
681             }
682         }
683
684         p_intf->p_sys->i_part =
685                 p_input->stream.p_selected_area->i_part;
686
687         p_intf->p_sys->b_chapter_update = 0;
688     }
689
690     for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
691     {
692         if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
693         {
694             p_audio_es = p_input->stream.pp_selected_es[i];
695         }
696         else if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
697         {
698             p_spu_es = p_input->stream.pp_selected_es[i];
699         }
700     }
701
702     vlc_mutex_unlock( &p_input->stream.stream_lock );
703
704     if( p_intf->p_sys->b_audio_update )
705     {
706         [self setupLangMenu: o_mi_language es: p_audio_es
707             category: AUDIO_ES selector: @selector(toggleLanguage:)];
708
709         p_intf->p_sys->b_audio_update = 0;
710     }
711
712     if( p_intf->p_sys->b_spu_update )
713     {
714         [self setupLangMenu: o_mi_subtitle es: p_spu_es
715             category: SPU_ES selector: @selector(toggleLanguage:)];
716
717         p_intf->p_sys->b_spu_update = 0;
718     }
719
720     if ( p_intf->p_sys->b_aout_update )
721     {
722         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
723                                                     FIND_ANYWHERE );
724
725         if ( p_aout != NULL )
726         {
727             vlc_value_t val;
728             val.b_bool = 0;
729
730             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
731
732             [self setupVarMenu: o_mi_channels target: (vlc_object_t *)p_aout
733                 var: "audio-channels" selector: @selector(toggleVar:)];
734
735             vlc_object_release( (vlc_object_t *)p_aout );
736         }
737
738         p_intf->p_sys->b_aout_update = 0;
739     }
740
741     if ( p_intf->p_sys->b_vout_update )
742     {
743         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
744                                                   FIND_ANYWHERE );
745
746         if ( p_vout != NULL )
747         {
748             vlc_value_t val;
749             val.b_bool = 0;
750
751             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
752
753             [self setupVarMenu: o_mi_screen target: (vlc_object_t *)p_vout
754                 var: "video-device" selector: @selector(toggleVar:)];
755
756             vlc_object_release( (vlc_object_t *)p_vout );
757         }
758
759         p_intf->p_sys->b_vout_update = 0;
760     }
761
762     vlc_mutex_lock( &p_input->stream.stream_lock );
763
764 #undef p_input
765 }
766
767 - (void)setupLangMenu:(NSMenuItem *)o_mi
768                       es:(es_descriptor_t *)p_es
769                       category:(int)i_cat
770                       selector:(SEL)pf_callback
771 {
772     int i, i_nb_items;
773     NSMenu * o_menu = [o_mi submenu];
774     intf_thread_t * p_intf = [NSApp getIntf];
775
776     /* remove previous language items */
777     i_nb_items = [o_menu numberOfItems];
778     for( i = 0; i < i_nb_items; i++ )
779     {
780         [o_menu removeItemAtIndex: 0];
781     }
782
783     /* make sensitive : we can't change it after we build the menu, and
784      * before, we don't yet how many items we will have. So make it
785      * always sensitive. --Meuuh */
786     [o_mi setEnabled: TRUE];
787
788     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
789
790 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
791     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
792     {
793         if( ( ES->i_cat == i_cat ) &&
794             ( !ES->p_pgrm ||
795               ES->p_pgrm ==
796                  p_intf->p_sys->p_input->stream.p_selected_program ) )
797         {
798             NSMenuItem * o_lmi;
799             NSString * o_title;
800
801             if( *ES->psz_desc )
802             {
803                 o_title = [NSString stringWithCString: ES->psz_desc];
804             }
805             else
806             {
807                 char psz_title[ 256 ];
808
809                 snprintf( psz_title, sizeof(psz_title), "Language 0x%x",
810                           ES->i_id );
811                 psz_title[sizeof(psz_title) - 1] = '\0';
812
813                 o_title = [NSString stringWithCString: psz_title];
814             }
815
816             o_lmi = [o_menu addItemWithTitle: o_title
817                 action: pf_callback keyEquivalent: @""];
818             [o_lmi setRepresentedObject: 
819                 [NSValue valueWithPointer: ES]];
820             [o_lmi setTarget: o_controls];
821             [o_lmi setTag: i_cat];
822
823             if( /*p_es == ES*/ ES->p_decoder_fifo != NULL )
824             {
825                 [o_lmi setState: NSOnState];
826             }
827         }
828     }
829 #undef ES
830
831     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
832 }
833
834 - (void)setupVarMenu:(NSMenuItem *)o_mi
835                      target:(vlc_object_t *)p_object
836                      var:(const char *)psz_variable
837                      selector:(SEL)pf_callback
838 {
839     int i, i_nb_items;
840     NSMenu * o_menu = [o_mi submenu];
841     vlc_value_t val;
842     int i_vals;
843     vlc_value_t * p_vals;
844     char * psz_value;
845
846     /* remove previous items */
847     i_nb_items = [o_menu numberOfItems];
848     for( i = 0; i < i_nb_items; i++ )
849     {
850         [o_menu removeItemAtIndex: 0];
851     }
852
853     if ( var_Get( p_object, psz_variable, &val ) < 0 )
854     {
855         return;
856     }
857     psz_value = val.psz_string;
858
859     if ( var_Change( p_object, psz_variable,
860                      VLC_VAR_GETLIST, &val ) < 0 )
861     {
862         free( psz_value );
863         return;
864     }
865
866     i_vals = ((vlc_value_t *)val.p_address)[0].i_int;
867     p_vals = &((vlc_value_t *)val.p_address)[1]; /* Starts at index 1 */
868
869     /* make (un)sensitive */
870     [o_mi setEnabled: (i_vals > 0)];
871
872     for ( i = 0; i < i_vals; i++ )
873     {
874         NSMenuItem * o_lmi;
875         NSString * o_title;
876
877         o_title = [NSString stringWithCString: p_vals[i].psz_string];
878         o_lmi = [o_menu addItemWithTitle: o_title
879                  action: pf_callback keyEquivalent: @""];
880         /* FIXME: this isn't 64-bit clean ! */
881         [o_lmi setTag: (int)psz_variable];
882         [o_lmi setRepresentedObject:
883             [NSValue valueWithPointer: p_object]];
884         [o_lmi setTarget: o_controls];
885
886         if ( !strcmp( psz_value, p_vals[i].psz_string ) )
887             [o_lmi setState: NSOnState];
888     }
889
890     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
891                 &val );
892
893     free( psz_value );
894 }
895
896 - (IBAction)clearRecentItems:(id)sender
897 {
898     [[NSDocumentController sharedDocumentController]
899                           clearRecentDocuments: nil];
900 }
901
902 - (void)openRecentItem:(id)sender
903 {
904     [self application: nil openFile: [sender title]]; 
905 }
906
907 - (IBAction)viewPreferences:(id)sender
908 {
909     if( o_prefs == nil )
910     {
911         o_prefs = [[VLCPrefs alloc] init];
912     }
913
914     [o_prefs createPrefPanel: @"main"];
915 }
916
917 - (id)getMIDevice
918 {
919     return o_mi_device;
920 }
921
922 @end
923
924 @implementation VLCMain (NSMenuValidation)
925
926 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
927 {
928     BOOL bEnabled = TRUE;
929
930     /* Recent Items Menu */
931
932     if( [[o_mi title] isEqualToString: _NS("Clear Menu")] )
933     {
934         NSMenu * o_menu = [o_mi_open_recent submenu];
935         int i_nb_items = [o_menu numberOfItems];
936         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
937                                                        recentDocumentURLs];
938         UInt32 i_nb_docs = [o_docs count];
939
940         if( i_nb_items > 1 )
941         {
942             while( --i_nb_items )
943             {
944                 [o_menu removeItemAtIndex: 0];
945             }
946         }
947
948         if( i_nb_docs > 0 )
949         {
950             NSURL * o_url;
951             NSString * o_doc;
952
953             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
954
955             while( TRUE )
956             {
957                 i_nb_docs--;
958
959                 o_url = [o_docs objectAtIndex: i_nb_docs];
960
961                 if( [o_url isFileURL] )
962                 {
963                     o_doc = [o_url path];
964                 }
965                 else
966                 {
967                     o_doc = [o_url absoluteString];
968                 }
969
970                 [o_menu insertItemWithTitle: o_doc
971                     action: @selector(openRecentItem:)
972                     keyEquivalent: @"" atIndex: 0]; 
973
974                 if( i_nb_docs == 0 )
975                 {
976                     break;
977                 }
978             } 
979         }
980         else
981         {
982             bEnabled = FALSE;
983         }
984     }
985
986     return( bEnabled );
987 }
988
989 @end
990
991 @implementation VLCMain (Internal)
992
993 - (void)handlePortMessage:(NSPortMessage *)o_msg
994 {
995     NSData * o_data;
996     NSValue * o_value;
997     NSInvocation * o_inv;
998     vout_thread_t * p_vout;
999  
1000     o_data = [[o_msg components] lastObject];
1001     o_inv = *((NSInvocation **)[o_data bytes]); 
1002     [o_inv getArgument: &o_value atIndex: 2];
1003     p_vout = (vout_thread_t *)[o_value pointerValue];
1004
1005     [p_vout->p_sys->o_lock lock];
1006     [o_inv invoke];
1007     [p_vout->p_sys->o_lock unlockWithCondition: 1];
1008 }
1009
1010 @end