]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
MacOS X port :
[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.19 2003/01/05 16:23:57 massiot Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <sys/param.h>                                    /* for MAXPATHLEN */
30 #include <string.h>
31
32 #include <QuickTime/QuickTime.h>
33
34 #include "intf.h"
35 #include "vout.h"
36 #include "prefs.h"
37 #include "playlist.h"
38
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 - (id)init
168 {
169     self = [super init];
170
171     if( self != nil )
172     {
173         o_prefs = nil;
174     }
175
176     return( self ); 
177 }
178
179 - (void)awakeFromNib
180 {
181     NSString * pTitle = [NSString
182         stringWithCString: VOUT_TITLE " (Cocoa)"];
183
184     [o_window setTitle: pTitle];
185
186     /* button controls */
187     [o_btn_playlist setToolTip: _NS("Playlist")];
188     [o_btn_prev setToolTip: _NS("Previous")];
189     [o_btn_slower setToolTip: _NS("Slower")];
190     [o_btn_play setToolTip: _NS("Play")];
191     [o_btn_pause setToolTip: _NS("Pause")];
192     [o_btn_stop setToolTip: _NS("Stop")];
193     [o_btn_faster setToolTip: _NS("Faster")];
194     [o_btn_next setToolTip: _NS("Next")];
195     [o_btn_prefs setToolTip: _NS("Preferences")];
196
197     /* messages panel */ 
198     [o_msgs_panel setTitle: _NS("Messages")];
199     [o_msgs_btn_ok setTitle: _NS("Close")];
200
201     /* main menu */
202     [o_mi_about setTitle: _NS("About vlc")];
203     [o_mi_prefs setTitle: _NS("Preferences")];
204     [o_mi_hide setTitle: _NS("Hide vlc")];
205     [o_mi_hide_others setTitle: _NS("Hide Others")];
206     [o_mi_show_all setTitle: _NS("Show All")];
207     [o_mi_quit setTitle: _NS("Quit vlc")];
208
209     [o_mu_file setTitle: _NS("File")];
210     [o_mi_open_generic setTitle: _NS("Open...")];
211     [o_mi_open_file setTitle: _NS("Open File...")];
212     [o_mi_open_disc setTitle: _NS("Open Disc...")];
213     [o_mi_open_net setTitle: _NS("Open Network...")];
214     [o_mi_open_recent setTitle: _NS("Open Recent")];
215     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
216
217     [o_mu_edit setTitle: _NS("Edit")];
218     [o_mi_cut setTitle: _NS("Cut")];
219     [o_mi_copy setTitle: _NS("Copy")];
220     [o_mi_paste setTitle: _NS("Paste")];
221     [o_mi_clear setTitle: _NS("Clear")];
222     [o_mi_select_all setTitle: _NS("Select All")];
223
224     [o_mu_view setTitle: _NS("View")];
225     [o_mi_playlist setTitle: _NS("Playlist")];
226     [o_mi_messages setTitle: _NS("Messages")];
227
228     [o_mu_controls setTitle: _NS("Controls")];
229     [o_mi_play setTitle: _NS("Play")];
230     [o_mi_pause setTitle: _NS("Pause")];
231     [o_mi_stop setTitle: _NS("Stop")];
232     [o_mi_faster setTitle: _NS("Faster")];
233     [o_mi_slower setTitle: _NS("Slower")];
234     [o_mi_previous setTitle: _NS("Previous")];
235     [o_mi_next setTitle: _NS("Next")];
236     [o_mi_loop setTitle: _NS("Loop")];
237     [o_mi_vol_up setTitle: _NS("Volume Up")];
238     [o_mi_vol_down setTitle: _NS("Volume Down")];
239     [o_mi_mute setTitle: _NS("Mute")];
240     [o_mi_channels setTitle: _NS("Channels")];
241     [o_mi_device setTitle: _NS("Device")];
242     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
243     [o_mi_screen setTitle: _NS("Screen")];
244     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
245     [o_mi_program setTitle: _NS("Program")];
246     [o_mi_title setTitle: _NS("Title")];
247     [o_mi_chapter setTitle: _NS("Chapter")];
248     [o_mi_language setTitle: _NS("Language")];
249     [o_mi_subtitle setTitle: _NS("Subtitles")];
250
251     [o_mu_window setTitle: _NS("Window")];
252     [o_mi_minimize setTitle: _NS("Minimize Window")];
253     [o_mi_close_window setTitle: _NS("Close Window")];
254     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
255
256     /* dock menu */
257     [o_dmi_play setTitle: _NS("Play")];
258     [o_dmi_pause setTitle: _NS("Pause")];
259     [o_dmi_stop setTitle: _NS("Stop")];
260
261     [self manageMode];
262 }
263
264 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
265 {
266     intf_thread_t * p_intf = [NSApp getIntf];
267
268     f_slider_old = f_slider = 0.0;
269     o_slider_lock = [[NSLock alloc] init];
270
271     [NSThread detachNewThreadSelector: @selector(manage)
272         toTarget: self withObject: nil];
273
274     [p_intf->p_sys->o_sendport setDelegate: self];
275     [[NSRunLoop currentRunLoop] 
276         addPort: p_intf->p_sys->o_sendport
277         forMode: NSDefaultRunLoopMode];
278 }
279
280 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
281 {
282     [o_playlist appendArray:
283         [NSArray arrayWithObject: o_filename] atPos: -1];
284
285     return( TRUE );
286 }
287
288 - (void)manage
289 {
290     NSDate * o_sleep_date;
291     intf_thread_t * p_intf = [NSApp getIntf];
292     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
293
294     while( !p_intf->b_die )
295     {
296         int i_start, i_stop;
297
298         vlc_mutex_lock( &p_intf->change_lock );
299
300         /* update the input */
301         if( p_intf->p_sys->p_input == NULL )
302         {
303             p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
304                                                               FIND_ANYWHERE );
305         }
306         else if( p_intf->p_sys->p_input->b_dead )
307         {
308             vlc_object_release( p_intf->p_sys->p_input );
309             p_intf->p_sys->p_input = NULL;
310         }
311
312         if( p_intf->p_sys->p_input != NULL )
313         {
314             vlc_bool_t b_need_menus = 0;
315             input_thread_t * p_input = p_intf->p_sys->p_input;
316             aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
317                                                         FIND_ANYWHERE );
318             vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
319                                                       FIND_ANYWHERE );
320
321             /* Disable screen saver. */
322             UpdateSystemActivity( UsrActivity );
323
324             vlc_mutex_lock( &p_input->stream.stream_lock );
325
326             if( !p_input->b_die )
327             {
328                 [self displayTime];
329
330                 /* New input or stream map change */
331                 if( p_input->stream.b_changed )
332                 {
333                     [self manageMode];
334                     b_need_menus = 1;
335                     p_intf->p_sys->b_playing = 1;
336                 }
337
338                 if( p_intf->p_sys->i_part !=
339                     p_input->stream.p_selected_area->i_part )
340                 {
341                     p_intf->p_sys->b_chapter_update = 1;
342                     b_need_menus = 1;
343                 }
344             }
345
346             if ( p_aout != NULL )
347             {
348                 vlc_value_t val;
349                 if ( var_Get( (vlc_object_t *)p_aout, "intf-change", &val )
350                       >= 0 && val.b_bool )
351                 {
352                     p_intf->p_sys->b_aout_update = 1;
353                     b_need_menus = 1;
354                 }
355                 vlc_object_release( (vlc_object_t *)p_aout );
356             }
357
358             if ( p_vout != NULL )
359             {
360                 vlc_value_t val;
361                 if ( var_Get( (vlc_object_t *)p_vout, "intf-change", &val )
362                       >= 0 && val.b_bool )
363                 {
364                     p_intf->p_sys->b_vout_update = 1;
365                     b_need_menus = 1;
366                 }
367                 vlc_object_release( (vlc_object_t *)p_vout );
368             }
369
370             if ( b_need_menus )
371                 [self setupMenus];
372
373             vlc_mutex_unlock( &p_input->stream.stream_lock );
374         }
375         else if( p_intf->p_sys->b_playing && !p_intf->b_die )
376         {
377             [self displayTime];
378             [self manageMode];
379             p_intf->p_sys->b_playing = 0;
380
381             if ( p_intf->p_sys->b_stopping )
382             {
383                 vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
384                                                           FIND_ANYWHERE );
385
386                 if ( p_vout != NULL )
387                 {
388                     vlc_object_detach( p_vout );
389                     vlc_object_release( p_vout );
390                     vout_Destroy( p_vout );
391                 }
392                 p_intf->p_sys->b_stopping = 0;
393             }
394         }
395
396         /* update the log window */
397         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
398         i_stop = *p_intf->p_sys->p_sub->pi_stop;
399         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
400
401         if( p_intf->p_sys->p_sub->i_start != i_stop )
402         {
403             NSColor *o_white = [NSColor whiteColor];
404             NSColor *o_red = [NSColor redColor];
405             NSColor *o_yellow = [NSColor yellowColor];
406             NSColor *o_gray = [NSColor grayColor];
407
408             unsigned int ui_length = [[o_messages string] length];
409
410             NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
411             static const char * ppsz_type[4] = { ": ", " error: ", 
412                                                  " warning: ", " debug: " }; 
413         
414             [o_messages setEditable: YES];
415             [o_messages setSelectedRange: NSMakeRange( ui_length, 0 )];
416             [o_messages scrollRangeToVisible: NSMakeRange( ui_length, 0 )];
417
418             for( i_start = p_intf->p_sys->p_sub->i_start;
419                  i_start != i_stop;
420                  i_start = (i_start+1) % VLC_MSG_QSIZE )
421             {
422                 NSString *o_msg;
423                 NSDictionary *o_attr;
424                 NSAttributedString *o_msg_color;
425                 int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
426
427                 o_attr = [NSDictionary dictionaryWithObject: o_gray
428                     forKey: NSForegroundColorAttributeName];
429                 o_msg = [NSString stringWithFormat: @"%s%s",
430                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module, 
431                     ppsz_type[i_type]];
432                 o_msg_color = [[NSAttributedString alloc]
433                     initWithString: o_msg attributes: o_attr];
434                 [o_messages insertText: o_msg_color];
435
436                 o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
437                     forKey: NSForegroundColorAttributeName];
438                 o_msg = [NSString stringWithCString:
439                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
440                 o_msg_color = [[NSAttributedString alloc]
441                     initWithString: o_msg attributes: o_attr];
442                 [o_messages insertText: o_msg_color];
443
444                 [o_messages insertText: @"\n"];
445             }
446
447             [o_messages setEditable: NO];
448
449             vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
450             p_intf->p_sys->p_sub->i_start = i_start;
451             vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
452         }
453
454         vlc_mutex_unlock( &p_intf->change_lock );
455
456         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: 0.1];
457         [NSThread sleepUntilDate: o_sleep_date];
458     }
459
460     [self terminate];
461
462     [o_pool release];
463 }
464
465 - (void)terminate
466 {
467     NSEvent * pEvent;
468     vout_thread_t * p_vout;
469     playlist_t * p_playlist;
470     intf_thread_t * p_intf = [NSApp getIntf];
471
472     /* release input */
473     if( p_intf->p_sys->p_input )
474     {
475         vlc_object_release( p_intf->p_sys->p_input );
476         p_intf->p_sys->p_input = NULL;
477     }
478
479     if( o_prefs != nil )
480     {
481         [o_prefs release];
482         o_prefs = nil;
483     }
484
485     /*
486      * Free playlists
487      */
488     msg_Dbg( p_intf, "removing all playlists" );
489     while( (p_playlist = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_PLAYLIST,
490                                           FIND_CHILD )) )
491     {
492         vlc_object_detach( p_playlist );
493         vlc_object_release( p_playlist );
494         playlist_Destroy( p_playlist );
495     }
496
497     /*
498      * Free video outputs
499      */
500     msg_Dbg( p_intf, "removing all video outputs" );
501     while( (p_vout = vlc_object_find( p_intf->p_vlc, 
502                                       VLC_OBJECT_VOUT, FIND_CHILD )) )
503     {
504         vlc_object_detach( p_vout );
505         vlc_object_release( p_vout );
506         vout_Destroy( p_vout );
507     }
508
509     [o_slider_lock release];
510
511     if( o_prefs != nil )
512     {
513         [o_prefs release];
514         o_prefs = nil;
515     }
516
517     [NSApp stop: nil];
518
519     /* write cached user defaults to disk */
520     [[NSUserDefaults standardUserDefaults] synchronize];
521
522     /* send a dummy event to break out of the event loop */
523     pEvent = [NSEvent mouseEventWithType: NSLeftMouseDown
524                 location: NSMakePoint( 1, 1 ) modifierFlags: 0
525                 timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
526                 context: [NSGraphicsContext currentContext] eventNumber: 1
527                 clickCount: 1 pressure: 0.0];
528     [NSApp postEvent: pEvent atStart: YES];
529 }
530
531 - (void)manageMode
532 {
533     vlc_bool_t b_input;
534     vlc_bool_t b_plmul = 0;
535     vlc_bool_t b_control = 0;
536     playlist_t * p_playlist = NULL;
537     intf_thread_t * p_intf = [NSApp getIntf];
538
539     if( ( b_input = ( p_intf->p_sys->p_input != NULL ) ) )
540     {
541         /* control buttons for free pace streams */
542         b_control = p_intf->p_sys->p_input->stream.b_pace_control;
543
544         /* get ready for menu regeneration */
545         p_intf->p_sys->b_program_update = 1;
546         p_intf->p_sys->b_title_update = 1;
547         p_intf->p_sys->b_chapter_update = 1;
548         p_intf->p_sys->b_audio_update = 1;
549         p_intf->p_sys->b_spu_update = 1;
550         p_intf->p_sys->i_part = 0;
551
552         p_intf->p_sys->p_input->stream.b_changed = 0;
553         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
554     }
555     else
556     {
557         /* unsensitize menus */
558         [o_mi_program setEnabled: FALSE];
559         [o_mi_title setEnabled: FALSE];
560         [o_mi_chapter setEnabled: FALSE];
561         [o_mi_language setEnabled: FALSE];
562         [o_mi_subtitle setEnabled: FALSE];
563         [o_mi_channels setEnabled: FALSE];
564         [o_mi_device setEnabled: FALSE];
565         [o_mi_screen setEnabled: FALSE];
566         [o_mi_close_window setEnabled: FALSE];
567     }
568
569     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, 
570                                               FIND_ANYWHERE ); 
571     if( p_playlist != NULL )
572     {
573         vlc_mutex_lock( &p_playlist->object_lock );
574         b_plmul = p_playlist->i_size > 1;
575         vlc_mutex_unlock( &p_playlist->object_lock );
576         vlc_object_release( p_playlist );
577     }
578
579     /* set control items */
580     [o_btn_stop setEnabled: b_input];
581     [o_btn_pause setEnabled: b_control];
582     [o_btn_faster setEnabled: b_control];
583     [o_btn_slower setEnabled: b_control];
584     [o_btn_prev setEnabled: b_plmul];
585     [o_btn_next setEnabled: b_plmul];
586
587     if ( (p_intf->p_sys->b_loop = config_GetInt( p_intf, "loop" )) )
588     {
589         [o_mi_loop setState: NSOnState];
590     }
591     else
592     {
593         [o_mi_loop setState: NSOffState];
594     }
595 }
596
597 - (void)setupMenus
598 {
599     unsigned int i, i_nb_items;
600     NSMenuItem * o_item;
601     NSString * o_menu_title;
602     char psz_title[ 256 ];
603
604     es_descriptor_t * p_audio_es = NULL;
605     es_descriptor_t * p_spu_es = NULL;
606
607     intf_thread_t * p_intf = [NSApp getIntf];
608
609     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
610     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
611                                      p_intf->p_sys->b_program_update;
612     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
613                                    p_intf->p_sys->b_program_update;
614
615 #define p_input (p_intf->p_sys->p_input)
616
617     if( p_intf->p_sys->b_program_update )
618     {
619         NSMenu * o_program;
620         SEL pf_toggle_program;
621         pgrm_descriptor_t * p_pgrm;
622
623         if( p_input->stream.p_new_program )
624         {
625             p_pgrm = p_input->stream.p_new_program;
626         }
627         else
628         {
629             p_pgrm = p_input->stream.p_selected_program;
630         }
631
632         o_program = [o_mi_program submenu];
633         pf_toggle_program = @selector(toggleProgram:);
634
635         /* remove previous program items */
636         i_nb_items = [o_program numberOfItems];
637         for( i = 0; i < i_nb_items; i++ )
638         {
639             [o_program removeItemAtIndex: 0];
640         }
641
642         /* make (un)sensitive */
643         [o_mi_program setEnabled: 
644             p_input->stream.i_pgrm_number > 1];
645
646         /* add program items */
647         for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
648         {
649             snprintf( psz_title, sizeof(psz_title), "id %d",
650                 p_input->stream.pp_programs[i]->i_number );
651             psz_title[sizeof(psz_title) - 1] = '\0';
652
653             o_menu_title = [NSString stringWithCString: psz_title];
654
655             o_item = [o_program addItemWithTitle: o_menu_title
656                 action: pf_toggle_program keyEquivalent: @""];
657             [o_item setTag: p_input->stream.pp_programs[i]->i_number];
658             [o_item setTarget: o_controls];
659
660             if( p_pgrm == p_input->stream.pp_programs[i] )
661             {
662                 [o_item setState: NSOnState];
663             }
664         }
665
666         p_intf->p_sys->b_program_update = 0;
667     }
668
669     if( p_intf->p_sys->b_title_update )
670     {
671         NSMenu * o_title;
672         SEL pf_toggle_title;
673
674         o_title = [o_mi_title submenu];
675         pf_toggle_title = @selector(toggleTitle:);
676
677         /* remove previous title items */
678         i_nb_items = [o_title numberOfItems];
679         for( i = 0; i < i_nb_items; i++ )
680         {
681             [o_title removeItemAtIndex: 0];
682         }
683
684         /* make (un)sensitive */
685         [o_mi_title setEnabled: 
686             p_input->stream.i_area_nb > 1];
687
688         /* add title items */
689         for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
690         {
691             snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
692                 p_input->stream.pp_areas[i]->i_part_nb );
693             psz_title[sizeof(psz_title) - 1] = '\0';
694
695             o_menu_title = [NSString stringWithCString: psz_title];
696
697             o_item = [o_title addItemWithTitle: o_menu_title
698                 action: pf_toggle_title keyEquivalent: @""];
699             [o_item setTag: i];
700             [o_item setTarget: o_controls];
701
702             if( ( p_input->stream.pp_areas[i] ==
703                 p_input->stream.p_selected_area ) )
704             {
705                 [o_item setState: NSOnState];
706             }
707         }
708
709         p_intf->p_sys->b_title_update = 0;
710     }
711
712     if( p_intf->p_sys->b_chapter_update )
713     {
714         NSMenu * o_chapter;
715         SEL pf_toggle_chapter;
716
717         o_chapter = [o_mi_chapter submenu];
718         pf_toggle_chapter = @selector(toggleChapter:);
719
720         /* remove previous chapter items */
721         i_nb_items = [o_chapter numberOfItems];
722         for( i = 0; i < i_nb_items; i++ )
723         {
724             [o_chapter removeItemAtIndex: 0];
725         }
726
727         /* make (un)sensitive */
728         [o_mi_chapter setEnabled: 
729             p_input->stream.p_selected_area->i_part_nb > 1];
730
731         /* add chapter items */
732         for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
733         {
734             snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
735             psz_title[sizeof(psz_title) - 1] = '\0';
736
737             o_menu_title = [NSString stringWithCString: psz_title];
738
739             o_item = [o_chapter addItemWithTitle: o_menu_title
740                 action: pf_toggle_chapter keyEquivalent: @""];
741             [o_item setTag: i + 1];
742             [o_item setTarget: o_controls];
743
744             if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
745             {
746                 [o_item setState: NSOnState];
747             }
748         }
749
750         p_intf->p_sys->i_part =
751                 p_input->stream.p_selected_area->i_part;
752
753         p_intf->p_sys->b_chapter_update = 0;
754     }
755
756     for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
757     {
758         if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
759         {
760             p_audio_es = p_input->stream.pp_selected_es[i];
761         }
762         else if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
763         {
764             p_spu_es = p_input->stream.pp_selected_es[i];
765         }
766     }
767
768     vlc_mutex_unlock( &p_input->stream.stream_lock );
769
770     if( p_intf->p_sys->b_audio_update )
771     {
772         [self setupLangMenu: o_mi_language es: p_audio_es
773             category: AUDIO_ES selector: @selector(toggleLanguage:)];
774
775         p_intf->p_sys->b_audio_update = 0;
776     }
777
778     if( p_intf->p_sys->b_spu_update )
779     {
780         [self setupLangMenu: o_mi_subtitle es: p_spu_es
781             category: SPU_ES selector: @selector(toggleLanguage:)];
782
783         p_intf->p_sys->b_spu_update = 0;
784     }
785
786     if ( p_intf->p_sys->b_aout_update )
787     {
788         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
789                                                     FIND_ANYWHERE );
790
791         if ( p_aout != NULL )
792         {
793             vlc_value_t val;
794             val.b_bool = 0;
795
796             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
797
798             [self setupVarMenu: o_mi_channels target: (vlc_object_t *)p_aout
799                 var: "audio-channels" selector: @selector(toggleVar:)];
800
801             [self setupVarMenu: o_mi_device target: (vlc_object_t *)p_aout
802                 var: "audio-device" selector: @selector(toggleVar:)];
803
804             vlc_object_release( (vlc_object_t *)p_aout );
805         }
806
807         p_intf->p_sys->b_aout_update = 0;
808     }
809
810     if ( p_intf->p_sys->b_vout_update )
811     {
812         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
813                                                   FIND_ANYWHERE );
814
815         if ( p_vout != NULL )
816         {
817             vlc_value_t val;
818             val.b_bool = 0;
819
820             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
821
822             [self setupVarMenu: o_mi_screen target: (vlc_object_t *)p_vout
823                 var: "video-device" selector: @selector(toggleVar:)];
824
825             vlc_object_release( (vlc_object_t *)p_vout );
826
827             [o_mi_close_window setEnabled: TRUE];
828         }
829
830         p_intf->p_sys->b_vout_update = 0;
831     }
832
833     vlc_mutex_lock( &p_input->stream.stream_lock );
834
835 #undef p_input
836 }
837
838 - (void)setupLangMenu:(NSMenuItem *)o_mi
839                       es:(es_descriptor_t *)p_es
840                       category:(int)i_cat
841                       selector:(SEL)pf_callback
842 {
843     unsigned int i, i_nb_items;
844     NSMenu * o_menu = [o_mi submenu];
845     intf_thread_t * p_intf = [NSApp getIntf];
846
847     /* remove previous language items */
848     i_nb_items = [o_menu numberOfItems];
849     for( i = 0; i < i_nb_items; i++ )
850     {
851         [o_menu removeItemAtIndex: 0];
852     }
853
854     /* make sensitive : we can't change it after we build the menu, and
855      * before, we don't yet how many items we will have. So make it
856      * always sensitive. --Meuuh */
857     [o_mi setEnabled: TRUE];
858
859     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
860
861 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
862     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
863     {
864         if( ( ES->i_cat == i_cat ) &&
865             ( !ES->p_pgrm ||
866               ES->p_pgrm ==
867                  p_intf->p_sys->p_input->stream.p_selected_program ) )
868         {
869             NSMenuItem * o_lmi;
870             NSString * o_title;
871
872             if( *ES->psz_desc )
873             {
874                 o_title = [NSString stringWithCString: ES->psz_desc];
875             }
876             else
877             {
878                 char psz_title[ 256 ];
879
880                 snprintf( psz_title, sizeof(psz_title), "Language 0x%x",
881                           ES->i_id );
882                 psz_title[sizeof(psz_title) - 1] = '\0';
883
884                 o_title = [NSString stringWithCString: psz_title];
885             }
886
887             o_lmi = [o_menu addItemWithTitle: o_title
888                 action: pf_callback keyEquivalent: @""];
889             [o_lmi setRepresentedObject: 
890                 [NSValue valueWithPointer: ES]];
891             [o_lmi setTarget: o_controls];
892             [o_lmi setTag: i_cat];
893
894             if( /*p_es == ES*/ ES->p_decoder_fifo != NULL )
895             {
896                 [o_lmi setState: NSOnState];
897             }
898         }
899     }
900 #undef ES
901
902     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
903 }
904
905 - (void)setupVarMenu:(NSMenuItem *)o_mi
906                      target:(vlc_object_t *)p_object
907                      var:(const char *)psz_variable
908                      selector:(SEL)pf_callback
909 {
910     int i, i_nb_items;
911     NSMenu * o_menu = [o_mi submenu];
912     vlc_value_t val;
913     char * psz_value;
914
915     /* remove previous items */
916     i_nb_items = [o_menu numberOfItems];
917     for( i = 0; i < i_nb_items; i++ )
918     {
919         [o_menu removeItemAtIndex: 0];
920     }
921
922     if ( var_Get( p_object, psz_variable, &val ) < 0 )
923     {
924         return;
925     }
926     psz_value = val.psz_string;
927
928     if ( var_Change( p_object, psz_variable,
929                      VLC_VAR_GETLIST, &val ) < 0 )
930     {
931         free( psz_value );
932         return;
933     }
934
935     /* make (un)sensitive */
936     [o_mi setEnabled: (val.p_list->i_count > 0)];
937
938     for ( i = 0; i < val.p_list->i_count; i++ )
939     {
940         NSMenuItem * o_lmi;
941         NSString * o_title;
942
943         o_title = [NSString stringWithCString: val.p_list->p_values[i].psz_string];
944         o_lmi = [o_menu addItemWithTitle: o_title
945                  action: pf_callback keyEquivalent: @""];
946         /* FIXME: this isn't 64-bit clean ! */
947         [o_lmi setTag: (int)psz_variable];
948         [o_lmi setRepresentedObject:
949             [NSValue valueWithPointer: p_object]];
950         [o_lmi setTarget: o_controls];
951
952         if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
953             [o_lmi setState: NSOnState];
954     }
955
956     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
957                 &val );
958
959     free( psz_value );
960 }
961
962 - (IBAction)clearRecentItems:(id)sender
963 {
964     [[NSDocumentController sharedDocumentController]
965                           clearRecentDocuments: nil];
966 }
967
968 - (void)openRecentItem:(id)sender
969 {
970     [self application: nil openFile: [sender title]]; 
971 }
972
973 - (IBAction)viewPreferences:(id)sender
974 {
975     if( o_prefs == nil )
976     {
977         o_prefs = [[VLCPrefs alloc] init];
978     }
979
980     [o_prefs createPrefPanel: @"main"];
981 }
982
983 - (IBAction)timesliderUpdate:(id)sender
984 {
985     switch( [[NSApp currentEvent] type] )
986     {
987         case NSLeftMouseDown:
988             [o_slider_lock lock];
989             break;
990
991         case NSLeftMouseUp:
992             f_slider = [sender floatValue];
993             [o_slider_lock unlock];
994             break;
995
996         default:
997             break;
998     }
999 }
1000
1001 - (void)displayTime
1002 {
1003     intf_thread_t * p_intf = [NSApp getIntf];
1004     input_thread_t * p_input = p_intf->p_sys->p_input;
1005
1006     if( p_input == NULL )
1007     {
1008         [o_timeslider setEnabled: FALSE];
1009         [o_timeslider setFloatValue: 0.0];
1010         [o_timefield setStringValue: @"0:00:00"]; 
1011
1012         return;
1013     }
1014
1015 #define p_area p_input->stream.p_selected_area
1016
1017     if( p_input->stream.b_changed )
1018     {
1019         [o_timeslider setEnabled: p_input->stream.b_seekable];
1020     }
1021     else if( p_intf->p_sys->b_playing )
1022     {
1023         NSString * o_time;
1024         char psz_time[ OFFSETTOTIME_MAX_SIZE ];
1025
1026         input_OffsetToTime( p_input, psz_time, p_area->i_tell );
1027
1028         o_time = [NSString stringWithCString: psz_time];
1029         [o_timefield setStringValue: o_time];
1030
1031         if( p_input->stream.b_seekable )
1032         {
1033             if( f_slider == f_slider_old )
1034             {
1035                 float f_updated = ( 100. * p_area->i_tell ) /
1036                                            p_area->i_size;
1037
1038                 if( f_slider != f_updated && [o_slider_lock tryLock] )
1039                 {
1040                     [o_timeslider setFloatValue: f_updated];
1041                     [o_slider_lock unlock]; 
1042                 }
1043             }
1044             else
1045             {
1046                 off_t i_seek = ( f_slider * p_area->i_size ) / 100;
1047
1048                 /* release the lock to be able to seek */
1049                 vlc_mutex_unlock( &p_input->stream.stream_lock );
1050                 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
1051                 vlc_mutex_lock( &p_input->stream.stream_lock ); 
1052
1053                 /* Update the old value */
1054                 f_slider_old = f_slider;
1055             }
1056         }
1057     }
1058 #undef p_area
1059 }
1060
1061 @end
1062
1063 @implementation VLCMain (NSMenuValidation)
1064
1065 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1066 {
1067     BOOL bEnabled = TRUE;
1068
1069     /* Recent Items Menu */
1070
1071     if( [[o_mi title] isEqualToString: _NS("Clear Menu")] )
1072     {
1073         NSMenu * o_menu = [o_mi_open_recent submenu];
1074         int i_nb_items = [o_menu numberOfItems];
1075         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1076                                                        recentDocumentURLs];
1077         UInt32 i_nb_docs = [o_docs count];
1078
1079         if( i_nb_items > 1 )
1080         {
1081             while( --i_nb_items )
1082             {
1083                 [o_menu removeItemAtIndex: 0];
1084             }
1085         }
1086
1087         if( i_nb_docs > 0 )
1088         {
1089             NSURL * o_url;
1090             NSString * o_doc;
1091
1092             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1093
1094             while( TRUE )
1095             {
1096                 i_nb_docs--;
1097
1098                 o_url = [o_docs objectAtIndex: i_nb_docs];
1099
1100                 if( [o_url isFileURL] )
1101                 {
1102                     o_doc = [o_url path];
1103                 }
1104                 else
1105                 {
1106                     o_doc = [o_url absoluteString];
1107                 }
1108
1109                 [o_menu insertItemWithTitle: o_doc
1110                     action: @selector(openRecentItem:)
1111                     keyEquivalent: @"" atIndex: 0]; 
1112
1113                 if( i_nb_docs == 0 )
1114                 {
1115                     break;
1116                 }
1117             } 
1118         }
1119         else
1120         {
1121             bEnabled = FALSE;
1122         }
1123     }
1124
1125     return( bEnabled );
1126 }
1127
1128 @end
1129
1130 @implementation VLCMain (Internal)
1131
1132 - (void)handlePortMessage:(NSPortMessage *)o_msg
1133 {
1134     NSData * o_data;
1135     NSValue * o_value;
1136     NSInvocation * o_inv;
1137     vout_thread_t * p_vout;
1138  
1139     o_data = [[o_msg components] lastObject];
1140     o_inv = *((NSInvocation **)[o_data bytes]); 
1141     [o_inv getArgument: &o_value atIndex: 2];
1142     p_vout = (vout_thread_t *)[o_value pointerValue];
1143
1144     [p_vout->p_sys->o_lock lock];
1145     [o_inv invoke];
1146     [p_vout->p_sys->o_lock unlockWithCondition: 1];
1147 }
1148
1149 @end