]> git.sesse.net Git - vlc/blob - plugins/macosx/intf_vlc_wrapper.m
b69e578ab1afce7c04f407b6cced8eec5a724c7a
[vlc] / plugins / macosx / intf_vlc_wrapper.m
1 /*****************************************************************************
2  * intf_vlc_wrapper.c: MacOS X plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: intf_vlc_wrapper.m,v 1.3 2002/05/18 13:33:44 massiot Exp $
6  *
7  * Authors: Florian G. Pflug <fgp@phlo.org>
8  *          Jon Lech Johansen <jon-vl@nanocrew.net>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 #include <stdlib.h>                                      /* malloc(), free() */
27 #include <sys/param.h>                                    /* for MAXPATHLEN */
28 #include <string.h>
29
30 #include <IOKit/storage/IOCDMedia.h>
31 #include <IOKit/storage/IODVDMedia.h>
32
33 #include <videolan/vlc.h>
34
35 #include "interface.h"
36 #include "intf_playlist.h"
37 #include "intf_eject.h"
38
39 #include "video.h"
40 #include "video_output.h"
41 #include "audio_output.h"
42
43 #include "stream_control.h"
44 #include "input_ext-intf.h"
45
46 #include "macosx.h"
47 #include "intf_open.h"
48 #include "intf_vlc_wrapper.h"
49
50 #include "netutils.h"
51
52 @implementation Intf_VLCWrapper
53
54 static Intf_VLCWrapper *o_intf = nil;
55
56 /* Initialization */
57
58 + (Intf_VLCWrapper *)instance
59 {
60     if( o_intf == nil )
61     {
62         o_intf = [[[Intf_VLCWrapper alloc] init] autorelease];
63     }
64
65     return( o_intf );
66 }
67
68 - (void)dealloc
69 {
70     o_intf = nil;
71     [super dealloc];
72 }
73
74 - (bool)manage
75 {
76     p_main->p_intf->pf_manage( p_main->p_intf );
77
78     if( p_main->p_intf->b_die )
79     {
80         /* Vout depends on intf */
81         input_EndBank();
82         vout_EndBank();
83         input_InitBank();
84         vout_InitBank();
85
86         return( 1 );
87     }
88
89     if( p_input_bank->pp_input[0] != NULL )
90     {
91         vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
92
93         if( !p_input_bank->pp_input[0]->b_die )
94         {
95             /* New input or stream map change */
96             if( p_input_bank->pp_input[0]->stream.b_changed ||
97                 p_main->p_intf->p_sys->i_part !=
98                 p_input_bank->pp_input[0]->stream.p_selected_area->i_part )
99             {
100                 [self setupMenus];
101             }
102         }
103
104         vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
105     }
106     else if ( !p_main->p_intf->p_sys->b_disabled_menus )
107     {
108         [self setupMenus];
109     }
110
111     return( 0 );
112 }
113
114 - (void)quit
115 {
116     p_main->p_intf->b_die = 1;
117 }
118
119 /* playlist control */
120     
121 - (bool)playlistPlay
122 {
123     if( p_input_bank->pp_input[0] != NULL )
124     {
125         input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
126         p_main->p_playlist->b_stopped = 0;
127     }
128     else
129     {
130         vlc_mutex_lock( &p_main->p_playlist->change_lock );
131
132         if( p_main->p_playlist->b_stopped )
133         {
134             if( p_main->p_playlist->i_size )
135             {
136                 vlc_mutex_unlock( &p_main->p_playlist->change_lock );
137                 intf_PlaylistJumpto( p_main->p_playlist,
138                                      p_main->p_playlist->i_index );
139             }
140             else
141             {
142                 vlc_mutex_unlock( &p_main->p_playlist->change_lock );
143                 [[Intf_Open instance] openFile: nil];
144             }
145         }
146         else
147         {
148             vlc_mutex_unlock( &p_main->p_playlist->change_lock );
149         }
150     }
151
152     return( TRUE );
153 }
154
155 - (void)playlistPause
156 {
157     if ( p_input_bank->pp_input[0] != NULL )
158     {
159         input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PAUSE );
160
161         vlc_mutex_lock( &p_main->p_playlist->change_lock );
162         p_main->p_playlist->b_stopped = 0;
163         vlc_mutex_unlock( &p_main->p_playlist->change_lock );
164     }
165 }
166     
167 - (void)playlistStop
168 {
169     if( p_input_bank->pp_input[0] != NULL )
170     {
171         /* end playing item */
172         p_input_bank->pp_input[0]->b_eof = 1;
173
174         /* update playlist */
175         vlc_mutex_lock( &p_main->p_playlist->change_lock );
176
177         p_main->p_playlist->i_index--;
178         p_main->p_playlist->b_stopped = 1;
179
180         vlc_mutex_unlock( &p_main->p_playlist->change_lock );
181     }
182 }
183
184 - (void)playlistNext
185 {
186     if( p_input_bank->pp_input[0] != NULL )
187     {
188         p_input_bank->pp_input[0]->b_eof = 1;
189     }
190 }
191
192 - (void)playlistPrev
193 {
194     if( p_input_bank->pp_input[0] != NULL )
195     {
196         /* FIXME: temporary hack */
197         intf_PlaylistPrev( p_main->p_playlist );
198         intf_PlaylistPrev( p_main->p_playlist );
199         p_input_bank->pp_input[0]->b_eof = 1;
200     }
201 }
202
203 - (void)playSlower
204 {
205     if( p_input_bank->pp_input[0] != NULL )
206     {
207         input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_SLOWER );
208
209         vlc_mutex_lock( &p_main->p_playlist->change_lock );
210         p_main->p_playlist->b_stopped = 0;
211         vlc_mutex_unlock( &p_main->p_playlist->change_lock );
212     }
213 }
214
215 - (void)playFaster
216 {
217     if( p_input_bank->pp_input[0] != NULL )
218     {
219         input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_FASTER );
220
221         vlc_mutex_lock( &p_main->p_playlist->change_lock );
222         p_main->p_playlist->b_stopped = 0;
223         vlc_mutex_unlock( &p_main->p_playlist->change_lock );
224     }
225 }
226
227 - (void)mute
228 {
229     if( p_aout_bank->pp_aout[0] == NULL ) return;
230
231     if( p_main->p_intf->p_sys->b_mute )
232     {
233         p_aout_bank->pp_aout[0]->i_volume = 
234                             p_main->p_intf->p_sys->i_saved_volume;
235     }
236     else
237     {
238         p_main->p_intf->p_sys->i_saved_volume = 
239                             p_aout_bank->pp_aout[0]->i_volume;
240         p_aout_bank->pp_aout[0]->i_volume = 0;
241     }
242     p_main->p_intf->p_sys->b_mute = !p_main->p_intf->p_sys->b_mute;
243 }
244
245 - (void)maxvolume
246 {
247     if( p_aout_bank->pp_aout[0] == NULL ) return;
248
249     if( p_main->p_intf->p_sys->b_mute )
250     {
251         p_main->p_intf->p_sys->i_saved_volume = VOLUME_MAX;
252     }
253     else
254     {
255         p_aout_bank->pp_aout[0]->i_volume = VOLUME_MAX;
256     }
257 }
258
259 - (void)fullscreen
260 {
261     if( p_vout_bank->pp_vout[0] != NULL )
262     {
263         p_vout_bank->pp_vout[0]->i_changes |= VOUT_FULLSCREEN_CHANGE;
264     }
265 }
266
267 - (void)eject
268 {
269     /* FIXME : this will only eject the first drive found */
270     NSArray * o_devices = GetEjectableMediaOfClass(kIODVDMediaClass);
271     const char * psz_device;
272
273     if ( p_input_bank->pp_input[0] != NULL &&
274          (p_input_bank->pp_input[0]->stream.i_method == INPUT_METHOD_VCD ||
275           p_input_bank->pp_input[0]->stream.i_method == INPUT_METHOD_DVD ||
276           p_input_bank->pp_input[0]->stream.i_method == INPUT_METHOD_DISC) )
277     {
278         intf_ErrMsg("error: cannot eject the disc while you're reading from it");
279         return;
280     }
281
282     if ( o_devices == nil )
283     {
284         o_devices = GetEjectableMediaOfClass(kIOCDMediaClass);
285     }
286
287     psz_device = [[o_devices objectAtIndex:0] cString];
288     intf_Eject( psz_device );
289 }
290
291 /* playback info */
292
293 #define p_area p_input_bank->pp_input[0]->stream.p_selected_area
294
295 - (NSString *)getTimeAsString
296 {
297     static char psz_currenttime[ OFFSETTOTIME_MAX_SIZE ];
298         
299     if( p_input_bank->pp_input[0] == NULL )
300     {
301         return [NSString stringWithCString:"00:00:00"];
302     }     
303    
304     input_OffsetToTime( p_input_bank->pp_input[0], 
305                         psz_currenttime, p_area->i_tell );        
306
307     return( [NSString stringWithCString: psz_currenttime] );
308 }
309     
310 - (float)getTimeAsFloat
311 {
312     float f_time = 0.0;
313
314     vlc_mutex_lock( &p_input_bank->lock );
315
316     if( p_input_bank->pp_input[0] != NULL )
317     {
318         f_time = (float)p_area->i_tell / (float)p_area->i_size;
319     }    
320
321     vlc_mutex_unlock( &p_input_bank->lock );
322
323     return( f_time );
324 }
325
326 - (void)setTimeAsFloat:(float)f_position
327 {
328     vlc_mutex_lock( &p_input_bank->lock );
329
330     if( p_input_bank->pp_input[0] != NULL )
331     {
332         input_Seek( p_input_bank->pp_input[0], p_area->i_size * f_position );
333     }
334
335     vlc_mutex_unlock( &p_input_bank->lock );
336 }
337
338 #undef p_area
339
340 - (bool)playlistPlaying
341 {
342     return( !p_main->p_playlist->b_stopped );
343 }
344
345 - (NSArray *)playlistAsArray
346 {
347     int i;
348     NSMutableArray* p_list = 
349         [NSMutableArray arrayWithCapacity: p_main->p_playlist->i_size];
350     
351     vlc_mutex_lock( &p_main->p_playlist->change_lock );
352
353     for( i = 0; i < p_main->p_playlist->i_size; i++ )
354     {
355         [p_list addObject: [NSString 
356             stringWithCString: p_main->p_playlist->p_item[i].psz_name]];
357     }
358
359     vlc_mutex_unlock( &p_main->p_playlist->change_lock );
360         
361     return( [NSArray arrayWithArray: p_list] );
362 }
363
364 /*
365 - (int)playlistLength
366 {
367     return( p_main->p_playlist->i_size );
368 }
369
370 - (NSString*)playlistItem:(int)i_pos
371 {
372     NSString *o_item = nil;
373
374     vlc_mutex_lock( &p_main->p_playlist->change_lock );
375     
376     if( i_pos < p_main->p_playlist->i_size )
377     {
378         o_item = [NSString 
379             stringWithCString: p_main->p_playlist->p_item[i_pos].psz_name];
380     }
381
382     vlc_mutex_unlock( &p_main->p_playlist->change_lock );
383
384     return( o_item );
385 }
386
387 - (void)playlistPlayItem:(int)i_item
388 {
389     [self playlistStop];
390
391     vlc_mutex_lock( &p_main->p_playlist->change_lock );
392
393     if( i_item<p_main->p_playlist->i_size )
394     {
395         p_main->p_playlist->i_index--;
396     }
397
398     vlc_mutex_unlock( &p_main->p_playlist->change_lock );        
399
400     [self playlistPlayCurrent];
401 }
402     
403 - (void)playlistAdd:(NSString *)o_filename
404 {
405     intf_PlaylistAdd( p_main->p_playlist, PLAYLIST_END, 
406                       [o_filename lossyCString] );
407 }
408     
409 - (void)clearPlaylist
410 {
411     int i;
412     
413     vlc_mutex_lock( &p_main->p_playlist->change_lock );
414
415     for( i = 0; i < p_main->p_playlist->i_size; i++ )
416     {
417         intf_PlaylistDelete( p_main->p_playlist, i );
418     }
419
420     vlc_mutex_unlock( &p_main->p_playlist->change_lock );        
421 }
422 */
423
424 /* open file/disc/network */
425
426 - (void)openFiles:(NSArray*)o_files
427 {
428     NSString *o_file;
429     int i_end = p_main->p_playlist->i_size;
430     NSEnumerator *o_enum = [o_files objectEnumerator];
431
432     while( ( o_file = (NSString *)[o_enum nextObject] ) )
433     {
434         intf_PlaylistAdd( p_main->p_playlist, PLAYLIST_END, 
435                           [o_file lossyCString] );
436     }
437
438     /* end current item, select first added item */
439     if( p_input_bank->pp_input[0] != NULL )
440     {
441         p_input_bank->pp_input[0]->b_eof = 1;
442     }
443
444     intf_PlaylistJumpto( p_main->p_playlist, i_end - 1 );
445 }
446
447 - (void)openDisc:(NSString*)o_type device:(NSString*)o_device title:(int)i_title chapter:(int)i_chapter
448 {
449     NSString *o_source;
450     int i_end = p_main->p_playlist->i_size;
451
452     o_source = [NSString stringWithFormat: @"%@:%@@%d,%d", 
453                     o_type, o_device, i_title, i_chapter];
454
455     intf_PlaylistAdd( p_main->p_playlist, PLAYLIST_END,
456                       [o_source lossyCString] );
457
458     /* stop current item, select added item */
459     if( p_input_bank->pp_input[0] != NULL )
460     {
461         p_input_bank->pp_input[0]->b_eof = 1;
462     }
463
464     intf_PlaylistJumpto( p_main->p_playlist, i_end - 1 );
465 }
466
467 - (void)openNet:(NSString*)o_protocol addr:(NSString*)o_addr port:(int)i_port baddr:(NSString*)o_baddr
468 {
469     NSString *o_source;
470     int i_end = p_main->p_playlist->i_size;
471
472     if( p_input_bank->pp_input[0] != NULL )
473     {
474         p_input_bank->pp_input[0]->b_eof = 1;
475     }
476
477     config_PutIntVariable( "network_channel", 0 );
478
479     if( o_baddr != nil )
480     {
481         o_source = [NSString stringWithFormat: @"%@://%@@:%i/%@",
482                         o_protocol, o_addr, i_port, o_baddr];
483     }
484     else
485     {
486         o_source = [NSString stringWithFormat: @"%@://%@@:%i",
487                         o_protocol, o_addr, i_port];
488     }
489
490     intf_PlaylistAdd( p_main->p_playlist, PLAYLIST_END,
491                       [o_source lossyCString] );
492
493     intf_PlaylistJumpto( p_main->p_playlist, i_end - 1 );
494 }
495
496 - (void)openNetChannel:(NSString*)o_addr port:(int)i_port
497 {
498     if( p_input_bank->pp_input[0] != NULL )
499     {
500         p_input_bank->pp_input[0]->b_eof = 1;
501     }
502
503     config_PutIntVariable( "network_channel", 1 );
504
505     if( p_main->p_channel == NULL )
506     {
507         network_ChannelCreate();
508     }
509
510     config_PutPszVariable( "channel_server", (char*)[o_addr lossyCString] );
511     config_PutIntVariable( "channel_port", i_port ); 
512 }
513
514 - (void)toggleProgram:(id)sender
515 {
516     NSMenuItem * o_item = (NSMenuItem *)sender;
517     input_thread_t * p_input = p_input_bank->pp_input[0];
518
519     if( [o_item state] == NSOffState )
520     {
521         u16 i_program_id = [o_item tag];
522
523         input_ChangeProgram( p_input, i_program_id );
524
525         vlc_mutex_lock( &p_input->stream.stream_lock );
526         [self setupMenus];
527         vlc_mutex_unlock( &p_input->stream.stream_lock );
528
529         input_SetStatus( p_input, INPUT_STATUS_PLAY );
530     }
531 }
532
533 - (void)toggleTitle:(id)sender
534 {
535     NSMenuItem * o_item = (NSMenuItem *)sender;
536     input_thread_t * p_input = p_input_bank->pp_input[0];
537
538     if( [o_item state] == NSOffState )
539     {
540         int i_title = [o_item tag];
541
542         input_ChangeArea( p_input,
543                           p_input->stream.pp_areas[i_title] );
544
545         vlc_mutex_lock( &p_input->stream.stream_lock );
546         [self setupMenus];
547         vlc_mutex_unlock( &p_input->stream.stream_lock );
548
549         input_SetStatus( p_input, INPUT_STATUS_PLAY );
550     }
551 }
552
553 - (void)toggleChapter:(id)sender
554 {
555     NSMenuItem * o_item = (NSMenuItem *)sender;
556     input_thread_t * p_input = p_input_bank->pp_input[0];
557
558     if( [o_item state] == NSOffState )
559     {
560         int i_chapter = [o_item tag];
561
562         p_input->stream.p_selected_area->i_part = i_chapter;
563         input_ChangeArea( p_input,
564                           p_input->stream.p_selected_area );
565
566         vlc_mutex_lock( &p_input->stream.stream_lock );
567         [self setupMenus];
568         vlc_mutex_unlock( &p_input->stream.stream_lock );
569
570         input_SetStatus( p_input, INPUT_STATUS_PLAY );
571     }
572 }
573
574 - (void)toggleLanguage:(id)sender
575 {
576     NSMenuItem * o_item = (NSMenuItem *)sender;
577     input_thread_t * p_input = p_input_bank->pp_input[0];
578
579     int i_es = [o_item tag];
580
581     if( [o_item state] == NSOnState )
582     {
583         /* We just have one ES to disable */
584         input_ToggleES( p_input, p_input->stream.pp_es[i_es], 0 );
585     }
586     else
587     {
588         /* Unselect the selected ES in the same class */
589         int i;
590         vlc_mutex_lock( &p_input->stream.stream_lock );
591         for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
592         {
593             if( p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
594             {
595                 vlc_mutex_unlock( &p_input->stream.stream_lock );
596                 input_ToggleES( p_input, p_input->stream.pp_selected_es[i], 0 );
597                 vlc_mutex_lock( &p_input->stream.stream_lock );
598                 break;
599             }
600         }
601         vlc_mutex_unlock( &p_input->stream.stream_lock );
602
603         /* Select the wanted ES */
604         input_ToggleES( p_input, p_input->stream.pp_es[i_es], 1 );
605     }
606
607     vlc_mutex_lock( &p_input->stream.stream_lock );
608     [self setupMenus];
609     vlc_mutex_unlock( &p_input->stream.stream_lock );
610
611     input_SetStatus( p_input, INPUT_STATUS_PLAY );
612 }
613
614 - (void)toggleSubtitle:(id)sender
615 {
616     NSMenuItem * o_item = (NSMenuItem *)sender;
617     input_thread_t * p_input = p_input_bank->pp_input[0];
618
619     int i_es = [o_item tag];
620
621     if( [o_item state] == NSOnState )
622     {
623         /* We just have one ES to disable */
624         input_ToggleES( p_input, p_input->stream.pp_es[i_es], 0 );
625     }
626     else
627     {
628         /* Unselect the selected ES in the same class */
629         int i;
630         vlc_mutex_lock( &p_input->stream.stream_lock );
631         for( i = 0; i < p_input->stream.i_selected_es_number; i++ )
632         {
633             if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
634             {
635                 vlc_mutex_unlock( &p_input->stream.stream_lock );
636                 input_ToggleES( p_input, p_input->stream.pp_selected_es[i], 0 );
637                 vlc_mutex_lock( &p_input->stream.stream_lock );
638                 break;
639             }
640         }
641         vlc_mutex_unlock( &p_input->stream.stream_lock );
642
643         /* Select the wanted ES */
644         input_ToggleES( p_input, p_input->stream.pp_es[i_es], 1 );
645     }
646
647     vlc_mutex_lock( &p_input->stream.stream_lock );
648     [self setupMenus];
649     vlc_mutex_unlock( &p_input->stream.stream_lock );
650
651     input_SetStatus( p_input, INPUT_STATUS_PLAY );
652 }
653
654 - (void)setupMenus
655 {
656     NSMenu *o_main_menu;
657     NSMenuItem *o_controls_item;
658     NSMenuItem *o_program_item, *o_title_item, *o_chapter_item, *o_language_item,
659                *o_subtitle_item;
660     input_thread_t * p_input = p_input_bank->pp_input[0];
661
662     o_main_menu  = [NSApp mainMenu];
663     o_controls_item  = [o_main_menu itemWithTitle: @"Controls"];
664     o_program_item = [[o_controls_item submenu] itemWithTitle: @"Program"]; 
665     o_title_item = [[o_controls_item submenu] itemWithTitle: @"Title"]; 
666     o_chapter_item = [[o_controls_item submenu] itemWithTitle: @"Chapter"]; 
667     o_language_item = [[o_controls_item submenu] itemWithTitle: @"Language"]; 
668     o_subtitle_item = [[o_controls_item submenu] itemWithTitle: @"Subtitles"]; 
669
670     if( p_input == NULL )
671     {
672         [o_program_item setEnabled:0];
673         [o_title_item setEnabled:0];
674         [o_chapter_item setEnabled:0];
675         [o_language_item setEnabled:0];
676         [o_subtitle_item setEnabled:0];
677     }
678     else
679     {
680         NSMenu *o_program, *o_title, *o_chapter, *o_language, *o_subtitle;
681         SEL pf_toggle_program, pf_toggle_title, pf_toggle_chapter,
682             pf_toggle_language, pf_toggle_subtitle;
683
684         int i, i_nb_items;
685         pgrm_descriptor_t * p_pgrm;
686
687         /* ----- PROGRAMS ----- */
688         if( p_input->stream.i_pgrm_number < 2 )
689         {
690             [o_program_item setEnabled:0];
691         }
692         else
693         {
694             [o_program_item setEnabled:1];
695             o_program = [o_program_item submenu];
696             pf_toggle_program = @selector(toggleProgram:);
697     
698             /* Remove previous program menu */
699             i_nb_items = [o_program numberOfItems];
700             for( i = 0; i < i_nb_items; i++ )
701             {
702                 [o_program removeItemAtIndex:0];
703             }
704     
705             if( p_input->stream.p_new_program )
706             {
707                 p_pgrm = p_input->stream.p_new_program;
708             }
709             else
710             {
711                 p_pgrm = p_input->stream.p_selected_program;
712             }
713     
714             /* Create program menu */
715             for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
716             {
717                 char psz_title[ 256 ];
718                 NSString * o_menu_title;
719                 NSMenuItem * o_item;
720     
721                 snprintf( psz_title, sizeof(psz_title), "id %d",
722                     p_input->stream.pp_programs[i]->i_number );
723                 psz_title[sizeof(psz_title) - 1] = '\0';
724     
725                 o_menu_title = [NSString stringWithCString: psz_title];
726     
727                 o_item = [o_program addItemWithTitle: o_menu_title
728                         action: pf_toggle_program keyEquivalent: @""];
729                 [o_item setTarget: self];
730                 [o_item setTag: p_input->stream.pp_programs[i]->i_number];
731                 if( p_pgrm == p_input->stream.pp_programs[i] )
732                 {
733                     [o_item setState: 1];
734                 }
735             }
736         }
737         vlc_mutex_unlock( &p_input->stream.stream_lock );
738         vlc_mutex_lock( &p_input->stream.stream_lock );
739
740         /* ----- TITLES ----- */
741         if( p_input->stream.i_area_nb < 2 )
742         {
743             [o_title_item setEnabled:0];
744         }
745         else
746         {
747             [o_title_item setEnabled:1];
748             o_title = [o_title_item submenu];
749             pf_toggle_title = @selector(toggleTitle:);
750     
751             /* Remove previous title menu */
752             i_nb_items = [o_title numberOfItems];
753             for( i = 0; i < i_nb_items; i++ )
754             {
755                 [o_title removeItemAtIndex:0];
756             }
757     
758             /* Create title menu */
759             for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
760             {
761                 char psz_title[ 256 ];
762                 NSString * o_menu_title;
763                 NSMenuItem * o_item;
764     
765                 snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
766                     p_input->stream.pp_areas[i]->i_part_nb );
767                 psz_title[sizeof(psz_title) - 1] = '\0';
768     
769                 o_menu_title = [NSString stringWithCString: psz_title];
770     
771                 o_item = [o_title addItemWithTitle: o_menu_title
772                         action: pf_toggle_title keyEquivalent: @""];
773                 [o_item setTag: i];
774                 [o_item setTarget: self];
775                 if( ( p_input->stream.pp_areas[i] ==
776                     p_input->stream.p_selected_area ) )
777                 {
778                     [o_item setState: 1];
779                 }
780             }
781         }
782         vlc_mutex_unlock( &p_input->stream.stream_lock );
783         vlc_mutex_lock( &p_input->stream.stream_lock );
784
785         /* ----- CHAPTERS ----- */
786         if( p_input->stream.p_selected_area->i_part_nb < 2 )
787         {
788             [o_chapter_item setEnabled:0];
789         }
790         else
791         {
792             [o_chapter_item setEnabled:1];
793             o_chapter = [o_chapter_item submenu];
794             pf_toggle_chapter = @selector(toggleChapter:);
795     
796             /* Remove previous chapter menu */
797             i_nb_items = [o_chapter numberOfItems];
798             for( i = 0; i < i_nb_items; i++ )
799             {
800                 [o_chapter removeItemAtIndex:0];
801             }
802     
803             /* Create chapter menu */
804             for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
805             {
806                 char psz_title[ 256 ];
807                 NSString * o_menu_title;
808                 NSMenuItem * o_item;
809     
810                 snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
811                 psz_title[sizeof(psz_title) - 1] = '\0';
812     
813                 o_menu_title = [NSString stringWithCString: psz_title];
814     
815                 o_item = [o_chapter addItemWithTitle: o_menu_title
816                         action: pf_toggle_chapter keyEquivalent: @""];
817                 [o_item setTag: i];
818                 [o_item setTarget: self];
819                 if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
820                 {
821                     [o_item setState: 1];
822                 }
823             }
824         }
825         p_main->p_intf->p_sys->i_part = p_input->stream.p_selected_area->i_part;
826         vlc_mutex_unlock( &p_input->stream.stream_lock );
827         vlc_mutex_lock( &p_input->stream.stream_lock );
828
829         /* ----- LANGUAGES & SUBTITLES ----- */
830         o_language = [o_language_item submenu];
831         o_subtitle = [o_subtitle_item submenu];
832         pf_toggle_language = @selector(toggleLanguage:);
833         pf_toggle_subtitle = @selector(toggleSubtitle:);
834
835         /* Remove previous language menu */
836         i_nb_items = [o_language numberOfItems];
837         for( i = 0; i < i_nb_items; i++ )
838         {
839             [o_language removeItemAtIndex:0];
840         }
841
842         /* Remove previous subtitle menu */
843         i_nb_items = [o_subtitle numberOfItems];
844         for( i = 0; i < i_nb_items; i++ )
845         {
846             [o_subtitle removeItemAtIndex:0];
847         }
848
849         /* Create language & subtitles menus */
850         for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
851         {
852             es_descriptor_t * p_es = p_input->stream.pp_es[i];
853             if( p_es->p_pgrm != NULL
854                  && p_es->p_pgrm != p_input->stream.p_selected_program )
855             {
856                 continue;
857             }
858
859             if( p_es->i_cat == AUDIO_ES )
860             {
861                 NSString * o_menu_title;
862                 NSMenuItem * o_item;
863
864                 if( *p_es->psz_desc )
865                 {
866                     o_menu_title = [NSString stringWithCString: p_es->psz_desc];
867                 }
868                 else
869                 {
870                     char psz_title[ 256 ];
871                     snprintf( psz_title, sizeof(psz_title), "Language 0x%x",
872                               p_es->i_id );
873                     psz_title[sizeof(psz_title) - 1] = '\0';
874     
875                     o_menu_title = [NSString stringWithCString: psz_title];
876                 }
877     
878                 o_item = [o_language addItemWithTitle: o_menu_title
879                         action: pf_toggle_language keyEquivalent: @""];
880                 [o_item setTag: i];
881                 [o_item setTarget: self];
882                 if( p_es->p_decoder_fifo != NULL )
883                 {
884                     [o_item setState: 1];
885                 }
886             }
887             else if( p_es->i_cat == SPU_ES )
888             {
889                 NSString * o_menu_title;
890                 NSMenuItem * o_item;
891
892                 if( *p_es->psz_desc )
893                 {
894                     o_menu_title = [NSString stringWithCString: p_es->psz_desc];
895                 }
896                 else
897                 {
898                     char psz_title[ 256 ];
899                     snprintf( psz_title, sizeof(psz_title), "Subtitle 0x%x",
900                               p_es->i_id );
901                     psz_title[sizeof(psz_title) - 1] = '\0';
902     
903                     o_menu_title = [NSString stringWithCString: psz_title];
904                 }
905     
906                 o_item = [o_subtitle addItemWithTitle: o_menu_title
907                         action: pf_toggle_subtitle keyEquivalent: @""];
908                 [o_item setTag: i];
909                 [o_item setTarget: self];
910                 if( p_es->p_decoder_fifo != NULL )
911                 {
912                     [o_item setState: 1];
913                 }
914             }
915         }
916
917         if( [o_language numberOfItems] )
918         {
919             [o_language_item setEnabled: 1];
920         }
921         else
922         {
923             [o_language_item setEnabled: 0];
924         }
925         if( [o_subtitle numberOfItems] )
926         {
927             [o_subtitle_item setEnabled: 1];
928         }
929         else
930         {
931             [o_subtitle_item setEnabled: 0];
932         }
933         p_input->stream.b_changed = 0;
934     }
935
936     p_main->p_intf->p_sys->b_disabled_menus = 1;
937 }
938
939 @end