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