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