]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* Mac OS X audio device discovery and selection, patch courtesy of
[vlc] / 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.3 2002/10/02 22:56:53 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 "playlist.h"
37 #include "asystm.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     UInt32 uiLength = (UInt32)strlen( psz );
139     NSData * o_data = [NSData dataWithBytes: psz length: uiLength];
140     NSString *o_str = [[NSString alloc] initWithData: o_data
141                                         encoding: i_encoding];
142     return( [o_str autorelease] );
143 }
144
145 - (void)setIntf:(intf_thread_t *)_p_intf
146 {
147     p_intf = _p_intf;
148 }
149
150 - (intf_thread_t *)getIntf
151 {
152     return( p_intf );
153 }
154
155 - (void)terminate:(id)sender
156 {
157     [self getIntf]->p_vlc->b_die = VLC_TRUE;
158 }
159
160 @end
161
162 /*****************************************************************************
163  * VLCMain implementation 
164  *****************************************************************************/
165 @implementation VLCMain
166
167 - (void)awakeFromNib
168 {
169     NSString * pTitle = [NSString
170         stringWithCString: VOUT_TITLE " (Cocoa)"];
171
172     [o_window setTitle: pTitle];
173
174     [o_msgs_panel setTitle: _NS("Messages")];
175     [o_msgs_btn_ok setTitle: _NS("Close")];
176
177     [o_mi_about setTitle: _NS("About vlc")];
178     [o_mi_hide setTitle: _NS("Hide vlc")];
179     [o_mi_hide_others setTitle: _NS("Hide Others")];
180     [o_mi_show_all setTitle: _NS("Show All")];
181     [o_mi_quit setTitle: _NS("Quit vlc")];
182
183     [o_mu_file setTitle: _NS("File")];
184     [o_mi_open_file setTitle: _NS("Open File")];
185     [o_mi_open_disc setTitle: _NS("Open Disc")];
186     [o_mi_open_net setTitle: _NS("Open Network")];
187     [o_mi_open_quickly setTitle: _NS("Open Quickly...")];
188     [o_mi_open_recent setTitle: _NS("Open Recent")];
189     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
190
191     [o_mu_edit setTitle: _NS("Edit")];
192     [o_mi_cut setTitle: _NS("Cut")];
193     [o_mi_copy setTitle: _NS("Copy")];
194     [o_mi_paste setTitle: _NS("Paste")];
195     [o_mi_clear setTitle: _NS("Clear")];
196     [o_mi_select_all setTitle: _NS("Select All")];
197
198     [o_mu_view setTitle: _NS("View")];
199     [o_mi_playlist setTitle: _NS("Playlist")];
200     [o_mi_messages setTitle: _NS("Messages")];
201
202     [o_mu_controls setTitle: _NS("Controls")];
203     [o_mi_play setTitle: _NS("Play")];
204     [o_mi_pause setTitle: _NS("Pause")];
205     [o_mi_stop setTitle: _NS("Stop")];
206     [o_mi_faster setTitle: _NS("Faster")];
207     [o_mi_slower setTitle: _NS("Slower")];
208     [o_mi_previous setTitle: _NS("Prev")];
209     [o_mi_next setTitle: _NS("Next")];
210     [o_mi_loop setTitle: _NS("Loop")];
211     [o_mi_vol_up setTitle: _NS("Volume Up")];
212     [o_mi_vol_down setTitle: _NS("Volume Down")];
213     [o_mi_mute setTitle: _NS("Mute")];
214     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
215     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
216     [o_mi_program setTitle: _NS("Program")];
217     [o_mi_title setTitle: _NS("Title")];
218     [o_mi_chapter setTitle: _NS("Chapter")];
219     [o_mi_language setTitle: _NS("Language")];
220     [o_mi_subtitle setTitle: _NS("Subtitles")];
221
222     [o_mu_window setTitle: _NS("Window")];
223     [o_mi_minimize setTitle: _NS("Minimize")];
224     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
225
226     /* dock menu */
227     [o_dmi_play setTitle: _NS("Play")];
228     [o_dmi_pause setTitle: _NS("Pause")];
229     [o_dmi_stop setTitle: _NS("Stop")];
230
231     [self manageMode];
232 }
233
234 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
235 {
236     intf_thread_t * p_intf = [NSApp getIntf];
237
238     [NSThread detachNewThreadSelector: @selector(manage)
239         toTarget: self withObject: nil];
240
241     [p_intf->p_sys->o_sendport setDelegate: self];
242     [[NSRunLoop currentRunLoop] 
243         addPort: p_intf->p_sys->o_sendport
244         forMode: NSDefaultRunLoopMode];
245
246     // Since we need the sound menu now, it's a good time to create the sound system class
247     asystm=[[MacOSXAudioSystem alloc] initWithGUI:self];
248     [asystm retain];
249     
250 }
251
252 - (void)noopAction:(id)sender {
253     // nothing
254 }
255
256 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
257 {
258     [o_playlist appendArray:
259         [NSArray arrayWithObject: o_filename] atPos: -1];
260
261     return( TRUE );
262 }
263
264 - (void)manage
265 {
266     NSDate * o_sleep_date;
267     intf_thread_t * p_intf = [NSApp getIntf];
268     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
269
270     while( !p_intf->b_die )
271     {
272         int i_start, i_stop;
273
274         vlc_mutex_lock( &p_intf->change_lock );
275
276         /* update the input */
277         if( p_intf->p_sys->p_input == NULL )
278         {
279             p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
280                                                               FIND_ANYWHERE );
281         }
282         else if( p_intf->p_sys->p_input->b_dead )
283         {
284             vlc_object_release( p_intf->p_sys->p_input );
285             p_intf->p_sys->p_input = NULL;
286         }
287
288         if( p_intf->p_sys->p_input )
289         {
290             input_thread_t *p_input = p_intf->p_sys->p_input;
291
292             vlc_mutex_lock( &p_input->stream.stream_lock );
293
294             if( !p_input->b_die )
295             {
296                 /* New input or stream map change */
297                 if( p_input->stream.b_changed )
298                 {
299                     [self manageMode];
300                     [self setupMenus];
301                     p_intf->p_sys->b_playing = 1;
302                 }
303
304                 if( p_intf->p_sys->i_part !=
305                     p_input->stream.p_selected_area->i_part )
306                 {
307                     p_intf->p_sys->b_chapter_update = 1;
308                     [self setupMenus];
309                 }
310             }
311
312             vlc_mutex_unlock( &p_input->stream.stream_lock );
313         }
314         else if( p_intf->p_sys->b_playing && !p_intf->b_die )
315         {
316             [self manageMode];
317             p_intf->p_sys->b_playing = 0;
318         }
319
320         /* update the log window */
321         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
322         i_stop = *p_intf->p_sys->p_sub->pi_stop;
323         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
324
325         if( p_intf->p_sys->p_sub->i_start != i_stop )
326         {
327             NSColor *o_white = [NSColor whiteColor];
328             NSColor *o_red = [NSColor redColor];
329             NSColor *o_yellow = [NSColor yellowColor];
330             NSColor *o_gray = [NSColor grayColor];
331
332             unsigned int ui_length = [[o_messages string] length];
333
334             NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
335             static const char * ppsz_type[4] = { ": ", " error: ", 
336                                                  " warning: ", " debug: " }; 
337         
338             [o_messages setEditable: YES];
339             [o_messages setSelectedRange: NSMakeRange( ui_length, 0 )];
340             [o_messages scrollRangeToVisible: NSMakeRange( ui_length, 0 )];
341
342             for( i_start = p_intf->p_sys->p_sub->i_start;
343                  i_start != i_stop;
344                  i_start = (i_start+1) % VLC_MSG_QSIZE )
345             {
346                 NSString *o_msg;
347                 NSDictionary *o_attr;
348                 NSAttributedString *o_msg_color;
349                 int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
350
351                 o_attr = [NSDictionary dictionaryWithObject: o_gray
352                     forKey: NSForegroundColorAttributeName];
353                 o_msg = [NSString stringWithFormat: @"%s%s",
354                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module, 
355                     ppsz_type[i_type]];
356                 o_msg_color = [[NSAttributedString alloc]
357                     initWithString: o_msg attributes: o_attr];
358                 [o_messages insertText: o_msg_color];
359
360                 o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
361                     forKey: NSForegroundColorAttributeName];
362                 o_msg = [NSString stringWithCString:
363                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
364                 o_msg_color = [[NSAttributedString alloc]
365                     initWithString: o_msg attributes: o_attr];
366                 [o_messages insertText: o_msg_color];
367
368                 [o_messages insertText: @"\n"];
369             }
370
371             [o_messages setEditable: NO];
372
373             vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
374             p_intf->p_sys->p_sub->i_start = i_start;
375             vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
376         }
377
378         vlc_mutex_unlock( &p_intf->change_lock );
379
380         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: 0.1];
381         [NSThread sleepUntilDate: o_sleep_date];
382     }
383
384     [self terminate];
385
386     [o_pool release];
387 }
388
389 - (void)terminate
390 {
391     NSEvent * pEvent;
392     vout_thread_t * p_vout;
393     playlist_t * p_playlist;
394     intf_thread_t * p_intf = [NSApp getIntf];
395
396     /* release input */
397     if( p_intf->p_sys->p_input )
398     {
399         vlc_object_release( p_intf->p_sys->p_input );
400         p_intf->p_sys->p_input = NULL;
401     }
402
403     /*
404      * Free playlists
405      */
406     msg_Dbg( p_intf, "removing all playlists" );
407     while( (p_playlist = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_PLAYLIST,
408                                           FIND_CHILD )) )
409     {
410         vlc_object_detach( p_playlist );
411         vlc_object_release( p_playlist );
412         playlist_Destroy( p_playlist );
413     }
414
415     /*
416      * Free video outputs
417      */
418     msg_Dbg( p_intf, "removing all video outputs" );
419     while( (p_vout = vlc_object_find( p_intf->p_vlc, 
420                                       VLC_OBJECT_VOUT, FIND_CHILD )) )
421     {
422         vlc_object_detach( p_vout );
423         vlc_object_release( p_vout );
424         vout_DestroyThread( p_vout );
425     }
426
427     [NSApp stop: nil];
428
429     /* write cached user defaults to disk */
430     [[NSUserDefaults standardUserDefaults] synchronize];
431
432     /* send a dummy event to break out of the event loop */
433     pEvent = [NSEvent mouseEventWithType: NSLeftMouseDown
434                 location: NSMakePoint( 1, 1 ) modifierFlags: 0
435                 timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
436                 context: [NSGraphicsContext currentContext] eventNumber: 1
437                 clickCount: 1 pressure: 0.0];
438     [NSApp postEvent: pEvent atStart: YES];
439
440     [asystm release];
441 }
442
443 - (void)manageMode
444 {
445     vlc_bool_t b_control = 0;
446     intf_thread_t * p_intf = [NSApp getIntf];
447
448     if( p_intf->p_sys->p_input )
449     {
450         /* control buttons for free pace streams */
451         b_control = p_intf->p_sys->p_input->stream.b_pace_control;
452
453         /* get ready for menu regeneration */
454         p_intf->p_sys->b_program_update = 1;
455         p_intf->p_sys->b_title_update = 1;
456         p_intf->p_sys->b_chapter_update = 1;
457         p_intf->p_sys->b_audio_update = 1;
458         p_intf->p_sys->b_spu_update = 1;
459         p_intf->p_sys->i_part = 0;
460
461         p_intf->p_sys->p_input->stream.b_changed = 0;
462         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
463     }
464     else
465     {
466         /* unsensitize menus */
467         [o_mi_program setEnabled: FALSE];
468         [o_mi_title setEnabled: FALSE];
469         [o_mi_chapter setEnabled: FALSE];
470         [o_mi_language setEnabled: FALSE];
471         [o_mi_subtitle setEnabled: FALSE];
472     }
473 }
474
475 - (void)setupMenus
476 {
477     int i, i_nb_items;
478     NSMenuItem * o_item;
479     NSString * o_menu_title;
480     char psz_title[ 256 ];
481
482     es_descriptor_t * p_audio_es = NULL;
483     es_descriptor_t * p_spu_es = NULL;
484
485     intf_thread_t * p_intf = [NSApp getIntf];
486
487     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
488     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
489                                      p_intf->p_sys->b_program_update;
490     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
491                                    p_intf->p_sys->b_program_update;
492
493 #define p_input (p_intf->p_sys->p_input)
494
495     if( p_intf->p_sys->b_program_update )
496     {
497         NSMenu * o_program;
498         SEL pf_toggle_program;
499         pgrm_descriptor_t * p_pgrm;
500
501         if( p_input->stream.p_new_program )
502         {
503             p_pgrm = p_input->stream.p_new_program;
504         }
505         else
506         {
507             p_pgrm = p_input->stream.p_selected_program;
508         }
509
510         o_program = [o_mi_program submenu];
511         pf_toggle_program = @selector(toggleProgram:);
512
513         /* remove previous program items */
514         i_nb_items = [o_program numberOfItems];
515         for( i = 0; i < i_nb_items; i++ )
516         {
517             [o_program removeItemAtIndex: 0];
518         }
519
520         /* make (un)sensitive */
521         [o_mi_program setEnabled: 
522             p_input->stream.i_pgrm_number > 1];
523
524         /* add program items */
525         for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
526         {
527             snprintf( psz_title, sizeof(psz_title), "id %d",
528                 p_input->stream.pp_programs[i]->i_number );
529             psz_title[sizeof(psz_title) - 1] = '\0';
530
531             o_menu_title = [NSString stringWithCString: psz_title];
532
533             o_item = [o_program addItemWithTitle: o_menu_title
534                 action: pf_toggle_program keyEquivalent: @""];
535             [o_item setTag: p_input->stream.pp_programs[i]->i_number];
536             [o_item setTarget: o_controls];
537
538             if( p_pgrm == p_input->stream.pp_programs[i] )
539             {
540                 [o_item setState: NSOnState];
541             }
542         }
543
544         p_intf->p_sys->b_program_update = 0;
545     }
546
547     if( p_intf->p_sys->b_title_update )
548     {
549         NSMenu * o_title;
550         SEL pf_toggle_title;
551
552         o_title = [o_mi_title submenu];
553         pf_toggle_title = @selector(toggleTitle:);
554
555         /* remove previous title items */
556         i_nb_items = [o_title numberOfItems];
557         for( i = 0; i < i_nb_items; i++ )
558         {
559             [o_title removeItemAtIndex: 0];
560         }
561
562         /* make (un)sensitive */
563         [o_mi_title setEnabled: 
564             p_input->stream.i_area_nb > 1];
565
566         /* add title items */
567         for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
568         {
569             snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
570                 p_input->stream.pp_areas[i]->i_part_nb );
571             psz_title[sizeof(psz_title) - 1] = '\0';
572
573             o_menu_title = [NSString stringWithCString: psz_title];
574
575             o_item = [o_title addItemWithTitle: o_menu_title
576                 action: pf_toggle_title keyEquivalent: @""];
577             [o_item setTag: i];
578             [o_item setTarget: o_controls];
579
580             if( ( p_input->stream.pp_areas[i] ==
581                 p_input->stream.p_selected_area ) )
582             {
583                 [o_item setState: NSOnState];
584             }
585         }
586
587         p_intf->p_sys->b_title_update = 0;
588     }
589
590     if( p_intf->p_sys->b_chapter_update )
591     {
592         NSMenu * o_chapter;
593         SEL pf_toggle_chapter;
594
595         o_chapter = [o_mi_chapter submenu];
596         pf_toggle_chapter = @selector(toggleChapter:);
597
598         /* remove previous chapter items */
599         i_nb_items = [o_chapter numberOfItems];
600         for( i = 0; i < i_nb_items; i++ )
601         {
602             [o_chapter removeItemAtIndex: 0];
603         }
604
605         /* make (un)sensitive */
606         [o_mi_chapter setEnabled: 
607             p_input->stream.p_selected_area->i_part_nb > 1];
608
609         /* add chapter items */
610         for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
611         {
612             snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
613             psz_title[sizeof(psz_title) - 1] = '\0';
614
615             o_menu_title = [NSString stringWithCString: psz_title];
616
617             o_item = [o_chapter addItemWithTitle: o_menu_title
618                 action: pf_toggle_chapter keyEquivalent: @""];
619             [o_item setTag: i + 1];
620             [o_item setTarget: o_controls];
621
622             if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
623             {
624                 [o_item setState: NSOnState];
625             }
626         }
627
628         p_intf->p_sys->i_part =
629                 p_input->stream.p_selected_area->i_part;
630
631         p_intf->p_sys->b_chapter_update = 0;
632     }
633
634     for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
635     {
636         if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
637         {
638             p_audio_es = p_input->stream.pp_selected_es[i];
639         }
640         else if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
641         {
642             p_spu_es = p_input->stream.pp_selected_es[i];
643         }
644     }
645
646     vlc_mutex_unlock( &p_input->stream.stream_lock );
647
648     if( p_intf->p_sys->b_audio_update )
649     {
650         [self setupLangMenu: o_mi_language es: p_audio_es
651             category: AUDIO_ES selector: @selector(toggleLanguage:)];
652
653         p_intf->p_sys->b_audio_update = 0;
654     }
655
656     if( p_intf->p_sys->b_spu_update )
657     {
658         [self setupLangMenu: o_mi_subtitle es: p_spu_es
659             category: SPU_ES selector: @selector(toggleLanguage:)];
660
661         p_intf->p_sys->b_spu_update = 0;
662     }
663
664     vlc_mutex_lock( &p_input->stream.stream_lock );
665
666 #undef p_input
667 }
668
669 - (void)setupLangMenu:(NSMenuItem *)o_mi
670                       es:(es_descriptor_t *)p_es
671                       category:(int)i_cat
672                       selector:(SEL)pf_callback
673 {
674     int i, i_nb_items;
675     NSMenu * o_menu = [o_mi submenu];
676     intf_thread_t * p_intf = [NSApp getIntf];
677
678     /* remove previous language items */
679     i_nb_items = [o_menu numberOfItems];
680     for( i = 0; i < i_nb_items; i++ )
681     {
682         [o_menu removeItemAtIndex: 0];
683     }
684
685     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
686
687 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
688     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
689     {
690         if( ( ES->i_cat == i_cat ) &&
691             ( !ES->p_pgrm ||
692               ES->p_pgrm ==
693                  p_intf->p_sys->p_input->stream.p_selected_program ) )
694         {
695             NSMenuItem * o_lmi;
696             NSString * o_title;
697
698             if( *ES->psz_desc )
699             {
700                 o_title = [NSString stringWithCString: ES->psz_desc];
701             }
702             else
703             {
704                 char psz_title[ 256 ];
705
706                 snprintf( psz_title, sizeof(psz_title), "Language 0x%x",
707                           ES->i_id );
708                 psz_title[sizeof(psz_title) - 1] = '\0';
709
710                 o_title = [NSString stringWithCString: psz_title];
711             }
712
713             o_lmi = [o_menu addItemWithTitle: o_title
714                 action: pf_callback keyEquivalent: @""];
715             [o_lmi setRepresentedObject: 
716                 [NSValue valueWithPointer: ES]];
717             [o_lmi setTarget: o_controls];
718             [o_lmi setTag: i_cat];
719
720             if( /*p_es == ES*/ ES->p_decoder_fifo != NULL )
721             {
722                 [o_lmi setState: NSOnState];
723             }
724         }
725     }
726 #undef ES
727
728     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
729
730     /* make (un)sensitive */
731     [o_mi setEnabled: 
732         [o_menu numberOfItems] ? TRUE : FALSE];
733 }
734
735 - (IBAction)clearRecentItems:(id)sender
736 {
737     [[NSDocumentController sharedDocumentController]
738                           clearRecentDocuments: nil];
739 }
740
741 - (void)openRecentItem:(id)sender
742 {
743     [self application: nil openFile: [sender title]]; 
744 }
745
746 @end
747
748 @implementation VLCMain (NSMenuValidation)
749
750 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
751 {
752     BOOL bEnabled = TRUE;
753
754     /* Recent Items Menu */
755
756     if( [[o_mi title] isEqualToString: _NS("Clear Menu")] )
757     {
758         NSMenu * o_menu = [o_mi_open_recent submenu];
759         int i_nb_items = [o_menu numberOfItems];
760         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
761                                                        recentDocumentURLs];
762         UInt32 i_nb_docs = [o_docs count];
763
764         if( i_nb_items > 1 )
765         {
766             while( --i_nb_items )
767             {
768                 [o_menu removeItemAtIndex: 0];
769             }
770         }
771
772         if( i_nb_docs > 0 )
773         {
774             NSURL * o_url;
775             NSString * o_doc;
776
777             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
778
779             while( TRUE )
780             {
781                 i_nb_docs--;
782
783                 o_url = [o_docs objectAtIndex: i_nb_docs];
784
785                 if( [o_url isFileURL] )
786                 {
787                     o_doc = [o_url path];
788                 }
789                 else
790                 {
791                     o_doc = [o_url absoluteString];
792                 }
793
794                 [o_menu insertItemWithTitle: o_doc
795                     action: @selector(openRecentItem:)
796                     keyEquivalent: @"" atIndex: 0]; 
797
798                 if( i_nb_docs == 0 )
799                 {
800                     break;
801                 }
802             } 
803         }
804         else
805         {
806             bEnabled = FALSE;
807         }
808     }
809
810     return( bEnabled );
811 }
812
813 @end
814
815 @implementation VLCMain (Internal)
816
817 - (void)handlePortMessage:(NSPortMessage *)o_msg
818 {
819     NSData * o_req;
820     vout_req_t * p_req;
821
822     o_req = [[o_msg components] lastObject];
823     p_req = *((vout_req_t **)[o_req bytes]);
824
825     [p_req->o_lock lock];
826
827     if( p_req->i_type == VOUT_REQ_CREATE_WINDOW )
828     {
829         VLCView * o_view;
830
831         p_req->p_vout->p_sys->o_window = [VLCWindow alloc];
832         [p_req->p_vout->p_sys->o_window setVout: p_req->p_vout];
833         [p_req->p_vout->p_sys->o_window setReleasedWhenClosed: YES];
834
835         if( p_req->p_vout->b_fullscreen )
836         {
837             [p_req->p_vout->p_sys->o_window 
838                 initWithContentRect: [[NSScreen mainScreen] frame] 
839                 styleMask: NSBorderlessWindowMask 
840                 backing: NSBackingStoreBuffered
841                 defer: NO screen: [NSScreen mainScreen]];
842
843             [p_req->p_vout->p_sys->o_window 
844                 setLevel: NSModalPanelWindowLevel];
845         }
846         else
847         {
848             unsigned int i_stylemask = NSTitledWindowMask |
849                                        NSMiniaturizableWindowMask |
850                                        NSResizableWindowMask;
851
852             [p_req->p_vout->p_sys->o_window 
853                 initWithContentRect: p_req->p_vout->p_sys->s_rect 
854                 styleMask: i_stylemask
855                 backing: NSBackingStoreBuffered
856                 defer: NO screen: [NSScreen mainScreen]];
857
858             if( !p_req->p_vout->p_sys->b_pos_saved )
859             {
860                 [p_req->p_vout->p_sys->o_window center];
861             }
862         }
863
864         o_view = [[VLCView alloc] init];
865         [o_view setVout: p_req->p_vout];
866         [o_view setMenu: o_mu_controls];
867         [p_req->p_vout->p_sys->o_window setContentView: o_view];
868         [o_view autorelease];
869
870         [o_view lockFocus];
871         p_req->p_vout->p_sys->p_qdport = [o_view qdPort];
872         [o_view unlockFocus];
873
874         [p_req->p_vout->p_sys->o_window setTitle: [NSString 
875             stringWithCString: VOUT_TITLE " (QuickTime)"]];
876         [p_req->p_vout->p_sys->o_window setAcceptsMouseMovedEvents: YES];
877         [p_req->p_vout->p_sys->o_window makeKeyAndOrderFront: nil];
878
879         p_req->i_result = 1;
880     }
881     else if( p_req->i_type == VOUT_REQ_DESTROY_WINDOW )
882     {
883         if( !p_req->p_vout->b_fullscreen )
884         {
885             NSRect s_rect;
886
887             s_rect = [[p_req->p_vout->p_sys->o_window contentView] frame];
888             p_req->p_vout->p_sys->s_rect.size = s_rect.size;
889
890             s_rect = [p_req->p_vout->p_sys->o_window frame];
891             p_req->p_vout->p_sys->s_rect.origin = s_rect.origin;
892
893             p_req->p_vout->p_sys->b_pos_saved = 1;
894         }
895
896         p_req->p_vout->p_sys->p_qdport = nil;
897         [p_req->p_vout->p_sys->o_window close];
898         p_req->p_vout->p_sys->o_window = nil;
899
900         p_req->i_result = 1;
901     }
902
903     [p_req->o_lock unlockWithCondition: 1];
904 }
905
906 @end
907