]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* Fullscreen button works again
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <hartman at videolan.org>
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 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32 #include <vlc_keys.h>
33
34 #include "intf.h"
35 #include "vout.h"
36 #include "prefs.h"
37 #include "playlist.h"
38 #include "controls.h"
39
40 /*****************************************************************************
41  * Local prototypes.
42  *****************************************************************************/
43 static void Run ( intf_thread_t *p_intf );
44
45 /*****************************************************************************
46  * OpenIntf: initialize interface
47  *****************************************************************************/
48 int E_(OpenIntf) ( vlc_object_t *p_this )
49 {
50     intf_thread_t *p_intf = (intf_thread_t*) p_this;
51
52     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
53     if( p_intf->p_sys == NULL )
54     {
55         return( 1 );
56     }
57
58     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
59
60     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
61
62     /* Put Cocoa into multithread mode as soon as possible.
63      * http://developer.apple.com/techpubs/macosx/Cocoa/
64      * TasksAndConcepts/ProgrammingTopics/Multithreading/index.html
65      * This thread does absolutely nothing at all. */
66     [NSThread detachNewThreadSelector:@selector(self) toTarget:[NSString string] withObject:nil];
67
68     p_intf->p_sys->o_sendport = [[NSPort port] retain];
69     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
70     p_intf->b_play = VLC_TRUE;
71     p_intf->pf_run = Run;
72
73     return( 0 );
74 }
75
76 /*****************************************************************************
77  * CloseIntf: destroy interface
78  *****************************************************************************/
79 void E_(CloseIntf) ( vlc_object_t *p_this )
80 {
81     intf_thread_t *p_intf = (intf_thread_t*) p_this;
82
83     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
84
85     [p_intf->p_sys->o_sendport release];
86     [p_intf->p_sys->o_pool release];
87
88     free( p_intf->p_sys );
89 }
90
91 /*****************************************************************************
92  * Run: main loop
93  *****************************************************************************/
94 static void Run( intf_thread_t *p_intf )
95 {
96     /* Do it again - for some unknown reason, vlc_thread_create() often
97      * fails to go to real-time priority with the first launched thread
98      * (???) --Meuuh */
99     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
100     [[VLCMain sharedInstance] setIntf: p_intf];
101     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
102     [NSApp run];
103     [[VLCMain sharedInstance] terminate];
104 }
105
106 int ExecuteOnMainThread( id target, SEL sel, void * p_arg )
107 {
108     int i_ret = 0;
109
110     //NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
111
112     if( [target respondsToSelector: @selector(performSelectorOnMainThread:
113                                              withObject:waitUntilDone:)] )
114     {
115         [target performSelectorOnMainThread: sel
116                 withObject: [NSValue valueWithPointer: p_arg]
117                 waitUntilDone: NO];
118     }
119     else if( NSApp != nil && [[VLCMain sharedInstance] respondsToSelector: @selector(getIntf)] )
120     {
121         NSValue * o_v1;
122         NSValue * o_v2;
123         NSArray * o_array;
124         NSPort * o_recv_port;
125         NSInvocation * o_inv;
126         NSPortMessage * o_msg;
127         intf_thread_t * p_intf;
128         NSConditionLock * o_lock;
129         NSMethodSignature * o_sig;
130
131         id * val[] = { &o_lock, &o_v2 };
132
133         p_intf = (intf_thread_t *)VLCIntf;
134
135         o_recv_port = [[NSPort port] retain];
136         o_v1 = [NSValue valueWithPointer: val];
137         o_v2 = [NSValue valueWithPointer: p_arg];
138
139         o_sig = [target methodSignatureForSelector: sel];
140         o_inv = [NSInvocation invocationWithMethodSignature: o_sig];
141         [o_inv setArgument: &o_v1 atIndex: 2];
142         [o_inv setTarget: target];
143         [o_inv setSelector: sel];
144
145         o_array = [NSArray arrayWithObject:
146             [NSData dataWithBytes: &o_inv length: sizeof(o_inv)]];
147         o_msg = [[NSPortMessage alloc]
148             initWithSendPort: p_intf->p_sys->o_sendport
149             receivePort: o_recv_port components: o_array];
150
151         o_lock = [[NSConditionLock alloc] initWithCondition: 0];
152         [o_msg sendBeforeDate: [NSDate distantPast]];
153         [o_lock lockWhenCondition: 1];
154         [o_lock unlock];
155         [o_lock release];
156
157         [o_msg release];
158         [o_recv_port release];
159     }
160     else
161     {
162         i_ret = 1;
163     }
164
165     //[o_pool release];
166
167     return( i_ret );
168 }
169
170 /*****************************************************************************
171  * playlistChanged: Callback triggered by the intf-change playlist
172  * variable, to let the intf update the playlist.
173  *****************************************************************************/
174 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
175                      vlc_value_t old_val, vlc_value_t new_val, void *param )
176 {
177     intf_thread_t * p_intf = VLCIntf;
178     p_intf->p_sys->b_playlist_update = TRUE;
179     p_intf->p_sys->b_intf_update = TRUE;
180     return VLC_SUCCESS;
181 }
182
183 /*****************************************************************************
184  * FullscreenChanged: Callback triggered by the fullscreen-change playlist
185  * variable, to let the intf update the controller.
186  *****************************************************************************/
187 int FullscreenChanged( vlc_object_t *p_this, const char *psz_variable,
188                      vlc_value_t old_val, vlc_value_t new_val, void *param )
189 {
190     intf_thread_t * p_intf = VLCIntf;
191     p_intf->p_sys->b_fullscreen_update = TRUE;
192     return VLC_SUCCESS;
193 }
194
195
196 static struct
197 {
198     unichar i_nskey;
199     unsigned int i_vlckey;
200 } nskeys_to_vlckeys[] =
201 {
202     { NSUpArrowFunctionKey, KEY_UP },
203     { NSDownArrowFunctionKey, KEY_DOWN },
204     { NSLeftArrowFunctionKey, KEY_LEFT },
205     { NSRightArrowFunctionKey, KEY_RIGHT },
206     { NSF1FunctionKey, KEY_F1 },
207     { NSF2FunctionKey, KEY_F2 },
208     { NSF3FunctionKey, KEY_F3 },
209     { NSF4FunctionKey, KEY_F4 },
210     { NSF5FunctionKey, KEY_F5 },
211     { NSF6FunctionKey, KEY_F6 },
212     { NSF7FunctionKey, KEY_F7 },
213     { NSF8FunctionKey, KEY_F8 },
214     { NSF9FunctionKey, KEY_F9 },
215     { NSF10FunctionKey, KEY_F10 },
216     { NSF11FunctionKey, KEY_F11 },
217     { NSF12FunctionKey, KEY_F12 },
218     { NSHomeFunctionKey, KEY_HOME },
219     { NSEndFunctionKey, KEY_END },
220     { NSPageUpFunctionKey, KEY_PAGEUP },
221     { NSPageDownFunctionKey, KEY_PAGEDOWN },
222     { NSTabCharacter, KEY_TAB },
223     { NSCarriageReturnCharacter, KEY_ENTER },
224     { NSEnterCharacter, KEY_ENTER },
225     { NSBackspaceCharacter, KEY_BACKSPACE },
226     { (unichar) ' ', KEY_SPACE },
227     { (unichar) 0x1b, KEY_ESC },
228     {0,0}
229 };
230
231 unichar VLCKeyToCocoa( unsigned int i_key )
232 {
233     unsigned int i;
234
235     for( i = 0; nskeys_to_vlckeys[i].i_vlckey != 0; i++ )
236     {
237         if( nskeys_to_vlckeys[i].i_vlckey == (i_key & ~KEY_MODIFIER) )
238         {
239             return nskeys_to_vlckeys[i].i_nskey;
240         }
241     }
242     return (unichar)(i_key & ~KEY_MODIFIER);
243 }
244
245 unsigned int CocoaKeyToVLC( unichar i_key )
246 {
247     unsigned int i;
248
249     for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
250     {
251         if( nskeys_to_vlckeys[i].i_nskey == i_key )
252         {
253             return nskeys_to_vlckeys[i].i_vlckey;
254         }
255     }
256     return (unsigned int)i_key;
257 }
258
259 unsigned int VLCModifiersToCocoa( unsigned int i_key )
260 {
261     unsigned int new = 0;
262     if( i_key & KEY_MODIFIER_COMMAND )
263         new |= NSCommandKeyMask;
264     if( i_key & KEY_MODIFIER_ALT )
265         new |= NSAlternateKeyMask;
266     if( i_key & KEY_MODIFIER_SHIFT )
267         new |= NSShiftKeyMask;
268     if( i_key & KEY_MODIFIER_CTRL )
269         new |= NSControlKeyMask;
270     return new;
271 }
272
273 /*****************************************************************************
274  * VLCMain implementation
275  *****************************************************************************/
276 @implementation VLCMain
277
278 static VLCMain *_o_sharedMainInstance = nil;
279
280 + (VLCMain *)sharedInstance
281 {
282     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
283 }
284
285 - (id)init
286 {
287     if( _o_sharedMainInstance) {
288         [self dealloc];
289     } else {
290         _o_sharedMainInstance = [super init];
291     }
292
293     return _o_sharedMainInstance;
294 }
295
296 - (void)setIntf: (intf_thread_t *)p_mainintf {
297     p_intf = p_mainintf;
298 }
299
300 - (intf_thread_t *)getIntf {
301     return p_intf;
302 }
303
304 - (void)awakeFromNib
305 {
306     unsigned int i_key = 0;
307     playlist_t *p_playlist;
308     vlc_value_t val;
309
310     [self initStrings];
311     [o_window setExcludedFromWindowsMenu: TRUE];
312     [o_msgs_panel setExcludedFromWindowsMenu: TRUE];
313     [o_msgs_panel setDelegate: self];
314
315     i_key = config_GetInt( p_intf, "key-quit" );
316     [o_mi_quit setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
317     [o_mi_quit setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
318     i_key = config_GetInt( p_intf, "key-play-pause" );
319     [o_mi_play setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
320     [o_mi_play setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
321     i_key = config_GetInt( p_intf, "key-stop" );
322     [o_mi_stop setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
323     [o_mi_stop setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
324     i_key = config_GetInt( p_intf, "key-faster" );
325     [o_mi_faster setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
326     [o_mi_faster setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
327     i_key = config_GetInt( p_intf, "key-slower" );
328     [o_mi_slower setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
329     [o_mi_slower setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
330     i_key = config_GetInt( p_intf, "key-prev" );
331     [o_mi_previous setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
332     [o_mi_previous setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
333     i_key = config_GetInt( p_intf, "key-next" );
334     [o_mi_next setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
335     [o_mi_next setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
336     i_key = config_GetInt( p_intf, "key-jump+10sec" );
337     [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
338     [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
339     i_key = config_GetInt( p_intf, "key-jump-10sec" );
340     [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
341     [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
342     i_key = config_GetInt( p_intf, "key-jump+1min" );
343     [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
344     [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
345     i_key = config_GetInt( p_intf, "key-jump-1min" );
346     [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
347     [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
348     i_key = config_GetInt( p_intf, "key-jump+5min" );
349     [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
350     [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
351     i_key = config_GetInt( p_intf, "key-jump-5min" );
352     [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
353     [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
354     i_key = config_GetInt( p_intf, "key-vol-up" );
355     [o_mi_vol_up setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
356     [o_mi_vol_up setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
357     i_key = config_GetInt( p_intf, "key-vol-down" );
358     [o_mi_vol_down setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
359     [o_mi_vol_down setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
360     i_key = config_GetInt( p_intf, "key-vol-mute" );
361     [o_mi_mute setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
362     [o_mi_mute setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
363     i_key = config_GetInt( p_intf, "key-fullscreen" );
364     [o_mi_fullscreen setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
365     [o_mi_fullscreen setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
366
367     var_Create( p_intf, "intf-change", VLC_VAR_BOOL );
368
369     [self setSubmenusEnabled: FALSE];
370     [self manageVolumeSlider];
371
372     p_playlist = (playlist_t *) vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
373
374     if( p_playlist )
375     {
376         /* Check if we need to start playing */
377         if( p_intf->b_play )
378         {
379             playlist_Play( p_playlist );
380         }
381         var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
382         val.b_bool = VLC_FALSE;
383
384         var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
385
386         [o_btn_fullscreen setState: ( var_Get( p_playlist, "fullscreen", &val )>=0 && val.b_bool )];
387         vlc_object_release( p_playlist );
388     }
389 }
390
391 - (void)initStrings
392 {
393     [o_window setTitle: _NS("VLC - Controller")];
394     [o_scrollfield setStringValue: _NS("VLC media player")];
395
396     /* button controls */
397     [o_btn_prev setToolTip: _NS("Previous")];
398     [o_btn_rewind setToolTip: _NS("Rewind")];
399     [o_btn_play setToolTip: _NS("Play")];
400     [o_btn_stop setToolTip: _NS("Stop")];
401     [o_btn_ff setToolTip: _NS("Fast Forward")];
402     [o_btn_next setToolTip: _NS("Next")];
403     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
404     [o_volumeslider setToolTip: _NS("Volume")];
405     [o_timeslider setToolTip: _NS("Position")];
406
407     /* messages panel */
408     [o_msgs_panel setTitle: _NS("Messages")];
409     [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog")];
410
411     /* main menu */
412     [o_mi_about setTitle: _NS("About VLC media player")];
413     [o_mi_prefs setTitle: _NS("Preferences...")];
414     [o_mi_add_intf setTitle: _NS("Add Interface")];
415     [o_mu_add_intf setTitle: _NS("Add Interface")];
416     [o_mi_services setTitle: _NS("Services")];
417     [o_mi_hide setTitle: _NS("Hide VLC")];
418     [o_mi_hide_others setTitle: _NS("Hide Others")];
419     [o_mi_show_all setTitle: _NS("Show All")];
420     [o_mi_quit setTitle: _NS("Quit VLC")];
421
422     [o_mu_file setTitle: _ANS("1:File")];
423     [o_mi_open_generic setTitle: _NS("Open File...")];
424     [o_mi_open_file setTitle: _NS("Quick Open File...")];
425     [o_mi_open_disc setTitle: _NS("Open Disc...")];
426     [o_mi_open_net setTitle: _NS("Open Network...")];
427     [o_mi_open_recent setTitle: _NS("Open Recent")];
428     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
429
430     [o_mu_edit setTitle: _NS("Edit")];
431     [o_mi_cut setTitle: _NS("Cut")];
432     [o_mi_copy setTitle: _NS("Copy")];
433     [o_mi_paste setTitle: _NS("Paste")];
434     [o_mi_clear setTitle: _NS("Clear")];
435     [o_mi_select_all setTitle: _NS("Select All")];
436
437     [o_mu_controls setTitle: _NS("Controls")];
438     [o_mi_play setTitle: _NS("Play")];
439     [o_mi_stop setTitle: _NS("Stop")];
440     [o_mi_faster setTitle: _NS("Faster")];
441     [o_mi_slower setTitle: _NS("Slower")];
442     [o_mi_previous setTitle: _NS("Previous")];
443     [o_mi_next setTitle: _NS("Next")];
444     [o_mi_random setTitle: _NS("Random")];
445     [o_mi_repeat setTitle: _NS("Repeat One")];
446     [o_mi_loop setTitle: _NS("Repeat All")];
447     [o_mi_fwd setTitle: _NS("Step Forward")];
448     [o_mi_bwd setTitle: _NS("Step Backward")];
449
450     [o_mi_program setTitle: _NS("Program")];
451     [o_mu_program setTitle: _NS("Program")];
452     [o_mi_title setTitle: _NS("Title")];
453     [o_mu_title setTitle: _NS("Title")];
454     [o_mi_chapter setTitle: _NS("Chapter")];
455     [o_mu_chapter setTitle: _NS("Chapter")];
456
457     [o_mu_audio setTitle: _NS("Audio")];
458     [o_mi_vol_up setTitle: _NS("Volume Up")];
459     [o_mi_vol_down setTitle: _NS("Volume Down")];
460     [o_mi_mute setTitle: _NS("Mute")];
461     [o_mi_audiotrack setTitle: _NS("Audio Track")];
462     [o_mu_audiotrack setTitle: _NS("Audio Track")];
463     [o_mi_channels setTitle: _NS("Audio Channels")];
464     [o_mu_channels setTitle: _NS("Audio Channels")];
465     [o_mi_device setTitle: _NS("Audio Device")];
466     [o_mu_device setTitle: _NS("Audio Device")];
467     [o_mi_visual setTitle: _NS("Visualizations")];
468     [o_mu_visual setTitle: _NS("Visualizations")];
469
470     [o_mu_video setTitle: _NS("Video")];
471     [o_mi_half_window setTitle: _NS("Half Size")];
472     [o_mi_normal_window setTitle: _NS("Normal Size")];
473     [o_mi_double_window setTitle: _NS("Double Size")];
474     [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
475     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
476     [o_mi_floatontop setTitle: _NS("Float on Top")];
477     [o_mi_videotrack setTitle: _NS("Video Track")];
478     [o_mu_videotrack setTitle: _NS("Video Track")];
479     [o_mi_screen setTitle: _NS("Video Device")];
480     [o_mu_screen setTitle: _NS("Video Device")];
481     [o_mi_subtitle setTitle: _NS("Subtitles Track")];
482     [o_mu_subtitle setTitle: _NS("Subtitles Track")];
483     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
484     [o_mu_deinterlace setTitle: _NS("Deinterlace")];
485     [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
486     [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
487
488     [o_mu_window setTitle: _NS("Window")];
489     [o_mi_minimize setTitle: _NS("Minimize Window")];
490     [o_mi_close_window setTitle: _NS("Close Window")];
491     [o_mi_controller setTitle: _NS("Controller")];
492     [o_mi_equalizer setTitle: _NS("Equalizer")];
493     [o_mi_playlist setTitle: _NS("Playlist")];
494     [o_mi_info setTitle: _NS("Info")];
495     [o_mi_messages setTitle: _NS("Messages")];
496
497     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
498
499     [o_mu_help setTitle: _NS("Help")];
500     [o_mi_readme setTitle: _NS("ReadMe...")];
501     [o_mi_documentation setTitle: _NS("Online Documentation")];
502     [o_mi_reportabug setTitle: _NS("Report a Bug")];
503     [o_mi_website setTitle: _NS("VideoLAN Website")];
504     [o_mi_license setTitle: _NS("License")];
505
506     /* dock menu */
507     [o_dmi_play setTitle: _NS("Play")];
508     [o_dmi_stop setTitle: _NS("Stop")];
509     [o_dmi_next setTitle: _NS("Next")];
510     [o_dmi_previous setTitle: _NS("Previous")];
511     [o_dmi_mute setTitle: _NS("Mute")];
512
513     /* error panel */
514     [o_error setTitle: _NS("Error")];
515     [o_err_lbl setStringValue: _NS("An error has occurred which probably prevented the execution of your request:")];
516     [o_err_bug_lbl setStringValue: _NS("If you believe that it is a bug, please follow the instructions at:")];
517     [o_err_btn_msgs setTitle: _NS("Open Messages Window")];
518     [o_err_btn_dismiss setTitle: _NS("Dismiss")];
519     [o_err_ckbk_surpress setTitle: _NS("Suppress further errors")];
520
521     [o_info_window setTitle: _NS("Info")];
522 }
523
524 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
525 {
526     o_msg_lock = [[NSLock alloc] init];
527     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
528
529     o_img_play = [[NSImage imageNamed: @"play"] retain];
530     o_img_play_pressed = [[NSImage imageNamed: @"play_blue"] retain];
531     o_img_pause = [[NSImage imageNamed: @"pause"] retain];
532     o_img_pause_pressed = [[NSImage imageNamed: @"pause_blue"] retain];
533
534     [p_intf->p_sys->o_sendport setDelegate: self];
535     [[NSRunLoop currentRunLoop]
536         addPort: p_intf->p_sys->o_sendport
537         forMode: NSDefaultRunLoopMode];
538
539     [NSTimer scheduledTimerWithTimeInterval: 0.5
540         target: self selector: @selector(manageIntf:)
541         userInfo: nil repeats: FALSE];
542
543     [NSThread detachNewThreadSelector: @selector(manage)
544         toTarget: self withObject: nil];
545
546     [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
547         var: "intf-add" selector: @selector(toggleVar:)];
548
549     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
550 }
551
552 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
553 {
554     NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
555     [o_playlist appendArray:
556         [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
557
558     return( TRUE );
559 }
560
561 - (NSString *)localizedString:(char *)psz
562 {
563     NSString * o_str = nil;
564
565     if( psz != NULL )
566     {
567         o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
568     }
569     if ( o_str == NULL )
570     {
571         msg_Err( VLCIntf, "could not translate: %s", psz );
572     }
573
574     return( o_str );
575 }
576
577 - (char *)delocalizeString:(NSString *)id
578 {
579     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
580                           allowLossyConversion: NO];
581     char * psz_string;
582
583     if ( o_data == nil )
584     {
585         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
586                      allowLossyConversion: YES];
587         psz_string = malloc( [o_data length] + 1 );
588         [o_data getBytes: psz_string];
589         psz_string[ [o_data length] ] = '\0';
590         msg_Err( VLCIntf, "cannot convert to wanted encoding: %s",
591                  psz_string );
592     }
593     else
594     {
595         psz_string = malloc( [o_data length] + 1 );
596         [o_data getBytes: psz_string];
597         psz_string[ [o_data length] ] = '\0';
598     }
599
600     return psz_string;
601 }
602
603 /* i_width is in pixels */
604 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
605 {
606     NSMutableString *o_wrapped;
607     NSString *o_out_string;
608     NSRange glyphRange, effectiveRange, charRange;
609     NSRect lineFragmentRect;
610     unsigned glyphIndex, breaksInserted = 0;
611
612     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
613         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
614         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
615     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
616     NSTextContainer *o_container = [[NSTextContainer alloc]
617         initWithContainerSize: NSMakeSize(i_width, 2000)];
618
619     [o_layout_manager addTextContainer: o_container];
620     [o_container release];
621     [o_storage addLayoutManager: o_layout_manager];
622     [o_layout_manager release];
623
624     o_wrapped = [o_in_string mutableCopy];
625     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
626
627     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
628             glyphIndex += effectiveRange.length) {
629         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
630                                             effectiveRange: &effectiveRange];
631         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
632                                     actualGlyphRange: &effectiveRange];
633         if ([o_wrapped lineRangeForRange:
634                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
635             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
636             breaksInserted++;
637         }
638     }
639     o_out_string = [NSString stringWithString: o_wrapped];
640     [o_wrapped release];
641     [o_storage release];
642
643     return o_out_string;
644 }
645
646
647 /*****************************************************************************
648  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
649  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
650  * otherwise ignore it and return NO (where it will get handled by Cocoa).
651  *****************************************************************************/
652 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
653 {
654     unichar key = 0;
655     vlc_value_t val;
656     unsigned int i_pressed_modifiers = 0;
657     struct hotkey *p_hotkeys;
658     int i;
659
660     val.i_int = 0;
661     p_hotkeys = p_intf->p_vlc->p_hotkeys;
662
663     i_pressed_modifiers = [o_event modifierFlags];
664
665     if( i_pressed_modifiers & NSShiftKeyMask )
666         val.i_int |= KEY_MODIFIER_SHIFT;
667     if( i_pressed_modifiers & NSControlKeyMask )
668         val.i_int |= KEY_MODIFIER_CTRL;
669     if( i_pressed_modifiers & NSAlternateKeyMask )
670         val.i_int |= KEY_MODIFIER_ALT;
671     if( i_pressed_modifiers & NSCommandKeyMask )
672         val.i_int |= KEY_MODIFIER_COMMAND;
673
674     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
675
676     val.i_int |= CocoaKeyToVLC( key );
677
678     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
679     {
680         if( p_hotkeys[i].i_key == val.i_int )
681         {
682             var_Set( p_intf->p_vlc, "key-pressed", val );
683             return YES;
684         }
685     }
686
687     return NO;
688 }
689
690 - (id)getControls
691 {
692     if ( o_controls )
693     {
694         return o_controls;
695     }
696     return nil;
697 }
698
699 - (id)getPlaylist
700 {
701     if ( o_playlist )
702     {
703         return o_playlist;
704     }
705     return nil;
706 }
707
708 - (id)getInfo
709 {
710     if ( o_info )
711     {
712         return o_info;
713     }
714     return  nil;
715 }
716
717 - (void)manage
718 {
719     NSDate * o_sleep_date;
720     playlist_t * p_playlist;
721     vlc_value_t val;
722
723     /* new thread requires a new pool */
724     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
725
726     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
727
728     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
729                                               FIND_ANYWHERE );
730
731     if( p_playlist != NULL )
732     {
733         var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
734         var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
735         var_AddCallback( p_playlist, "playlist-current", PlaylistChanged, self );
736
737         vlc_object_release( p_playlist );
738     }
739
740     while( !p_intf->b_die )
741     {
742         vlc_mutex_lock( &p_intf->change_lock );
743
744 #define p_input p_intf->p_sys->p_input
745
746         if( p_input == NULL )
747         {
748             p_input = (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
749                                            FIND_ANYWHERE );
750
751             /* Refresh the interface */
752             if( p_input )
753             {
754                 msg_Dbg( p_intf, "input has changed, refreshing interface" );
755                 p_intf->p_sys->b_input_update = VLC_TRUE;
756             }
757         }
758         else if( p_input->b_die || p_input->b_dead )
759         {
760             /* input stopped */
761             p_intf->p_sys->b_intf_update = VLC_TRUE;
762             p_intf->p_sys->i_play_status = END_S;
763             [o_scrollfield setStringValue: _NS("VLC media player") ];
764             vlc_object_release( p_input );
765             p_input = NULL;
766         }
767 #undef p_input
768
769         vlc_mutex_unlock( &p_intf->change_lock );
770
771         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: .1];
772         [NSThread sleepUntilDate: o_sleep_date];
773     }
774
775     [self terminate];
776     [o_pool release];
777 }
778
779 - (void)manageIntf:(NSTimer *)o_timer
780 {
781     vlc_value_t val;
782
783     if( p_intf->p_vlc->b_die == VLC_TRUE )
784     {
785         [o_timer invalidate];
786         return;
787     }
788
789 #define p_input p_intf->p_sys->p_input
790     if( p_intf->p_sys->b_input_update )
791     {
792         /* Called when new input is opened */
793         p_intf->p_sys->b_current_title_update = VLC_TRUE;
794         p_intf->p_sys->b_intf_update = VLC_TRUE;
795         p_intf->p_sys->b_input_update = VLC_FALSE;
796     }
797     if( p_intf->p_sys->b_intf_update )
798     {
799         vlc_bool_t b_input = VLC_FALSE;
800         vlc_bool_t b_plmul = VLC_FALSE;
801         vlc_bool_t b_control = VLC_FALSE;
802         vlc_bool_t b_seekable = VLC_FALSE;
803         vlc_bool_t b_chapters = VLC_FALSE;
804
805         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
806                                                    FIND_ANYWHERE );
807         b_plmul = p_playlist->i_size > 1;
808
809         vlc_object_release( p_playlist );
810
811         if( ( b_input = ( p_input != NULL ) ) )
812         {
813             /* seekable streams */
814             var_Get( p_input, "seekable", &val);
815             b_seekable = val.b_bool;
816
817             /* check wether slow/fast motion is possible*/
818             b_control = p_input->input.b_can_pace_control;
819
820             /* chapters & titles */
821             //b_chapters = p_input->stream.i_area_nb > 1;
822         }
823
824         [o_btn_stop setEnabled: b_input];
825         [o_btn_ff setEnabled: b_seekable];
826         [o_btn_rewind setEnabled: b_seekable];
827         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
828         [o_btn_next setEnabled: (b_plmul || b_chapters)];
829
830         [o_timeslider setFloatValue: 0.0];
831         [o_timeslider setEnabled: b_seekable];
832         [o_timefield setStringValue: @"0:00:00"];
833
834         p_intf->p_sys->b_intf_update = VLC_FALSE;
835     }
836
837     if ( p_intf->p_sys->b_playlist_update )
838     {
839        [o_playlist playlistUpdated];
840         p_intf->p_sys->b_playlist_update = VLC_FALSE;
841     }
842
843     if( p_intf->p_sys->b_fullscreen_update )
844     {
845         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
846                                                    FIND_ANYWHERE );
847
848         [o_btn_fullscreen setState: ( var_Get( p_playlist, "fullscreen", &val )>=0 && val.b_bool ) ];
849
850         vlc_object_release( p_playlist );
851
852         p_intf->p_sys->b_fullscreen_update = VLC_FALSE;
853     }
854
855     if( p_input && !p_input->b_die )
856     {
857         vlc_value_t val;
858
859         if( p_intf->p_sys->b_current_title_update )
860         {
861             NSString *o_temp;
862             vout_thread_t *p_vout;
863             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
864                                                        FIND_ANYWHERE );
865
866             if( p_playlist == NULL )
867             {
868                 return;
869             }
870
871             vlc_mutex_lock( &p_playlist->object_lock );
872             o_temp = [NSString stringWithUTF8String:
873                 p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
874             if( o_temp == NULL )
875                 o_temp = [NSString stringWithCString:
876                     p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
877             vlc_mutex_unlock( &p_playlist->object_lock );
878             [o_scrollfield setStringValue: o_temp ];
879
880
881             /*p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
882                                                     FIND_ANYWHERE );
883             if( p_vout != NULL )
884             {
885                 id o_vout_wnd;
886                 NSEnumerator * o_enum = [[NSApp orderedWindows] objectEnumerator];
887
888                 while( ( o_vout_wnd = [o_enum nextObject] ) )
889                 {
890                     if( [[o_vout_wnd className] isEqualToString: @"VLCWindow"] )
891                     {
892                         ;[o_vout_wnd updateTitle];
893                     }
894                 }
895                 vlc_object_release( (vlc_object_t *)p_vout );
896             }*/
897             [o_playlist updateRowSelection];
898             vlc_object_release( p_playlist );
899             p_intf->p_sys->b_current_title_update = FALSE;
900         }
901
902         if( p_input && [o_timeslider isEnabled] )
903         {
904             /* Update the slider */
905             vlc_value_t time;
906             NSString * o_time;
907             mtime_t i_seconds;
908             vlc_value_t pos;
909             float f_updated;
910
911             var_Get( p_input, "position", &pos );
912             f_updated = 10000. * pos.f_float;
913             [o_timeslider setFloatValue: f_updated];
914
915             var_Get( p_input, "time", &time );
916             i_seconds = time.i_time / 1000000;
917
918             o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
919                             (int) (i_seconds / (60 * 60)),
920                             (int) (i_seconds / 60 % 60),
921                             (int) (i_seconds % 60)];
922             [o_timefield setStringValue: o_time];
923         }
924
925         /* Manage volume status */
926         [self manageVolumeSlider];
927
928         /* Manage Playing status */
929         var_Get( p_input, "state", &val );
930         if( p_intf->p_sys->i_play_status != val.i_int )
931         {
932             p_intf->p_sys->i_play_status = val.i_int;
933             [self playStatusUpdated: p_intf->p_sys->i_play_status];
934         }
935     }
936     else
937     {
938         p_intf->p_sys->i_play_status = END_S;
939         p_intf->p_sys->b_intf_update = VLC_TRUE;
940         [self playStatusUpdated: p_intf->p_sys->i_play_status];
941         [self setSubmenusEnabled: FALSE];
942     }
943
944 #undef p_input
945
946     [self updateMessageArray];
947
948     [NSTimer scheduledTimerWithTimeInterval: 0.3
949         target: self selector: @selector(manageIntf:)
950         userInfo: nil repeats: FALSE];
951 }
952
953 - (void)setupMenus
954 {
955 #define p_input p_intf->p_sys->p_input
956     if( p_input != NULL )
957     {
958         [o_controls setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
959             var: "program" selector: @selector(toggleVar:)];
960
961         [o_controls setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
962             var: "title" selector: @selector(toggleVar:)];
963
964         [o_controls setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
965             var: "chapter" selector: @selector(toggleVar:)];
966
967         [o_controls setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
968             var: "audio-es" selector: @selector(toggleVar:)];
969
970         [o_controls setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
971             var: "video-es" selector: @selector(toggleVar:)];
972
973         [o_controls setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
974             var: "spu-es" selector: @selector(toggleVar:)];
975
976         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
977                                                     FIND_ANYWHERE );
978         if ( p_aout != NULL )
979         {
980             [o_controls setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
981                 var: "audio-channels" selector: @selector(toggleVar:)];
982
983             [o_controls setupVarMenuItem: o_mi_device target: (vlc_object_t *)p_aout
984                 var: "audio-device" selector: @selector(toggleVar:)];
985
986             [o_controls setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
987                 var: "visual" selector: @selector(toggleVar:)];
988             vlc_object_release( (vlc_object_t *)p_aout );
989         }
990
991         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
992                                                             FIND_ANYWHERE );
993
994         if ( p_vout != NULL )
995         {
996             vlc_object_t * p_dec_obj;
997
998             [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
999                 var: "video-device" selector: @selector(toggleVar:)];
1000
1001             [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
1002                 var: "deinterlace" selector: @selector(toggleVar:)];
1003
1004             p_dec_obj = (vlc_object_t *)vlc_object_find(
1005                                                  (vlc_object_t *)p_vout,
1006                                                  VLC_OBJECT_DECODER,
1007                                                  FIND_PARENT );
1008             if ( p_dec_obj != NULL )
1009             {
1010                [o_controls setupVarMenuItem: o_mi_ffmpeg_pp target:
1011                     (vlc_object_t *)p_dec_obj var:"ffmpeg-pp-q" selector:
1012                     @selector(toggleVar:)];
1013
1014                 vlc_object_release(p_dec_obj);
1015             }
1016             vlc_object_release( (vlc_object_t *)p_vout );
1017         }
1018     }
1019 #undef p_input
1020 }
1021
1022 - (void)updateMessageArray
1023 {
1024     int i_start, i_stop;
1025     vlc_value_t quiet;
1026
1027     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1028     i_stop = *p_intf->p_sys->p_sub->pi_stop;
1029     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1030
1031     if( p_intf->p_sys->p_sub->i_start != i_stop )
1032     {
1033         NSColor *o_white = [NSColor whiteColor];
1034         NSColor *o_red = [NSColor redColor];
1035         NSColor *o_yellow = [NSColor yellowColor];
1036         NSColor *o_gray = [NSColor grayColor];
1037
1038         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1039         static const char * ppsz_type[4] = { ": ", " error: ",
1040                                              " warning: ", " debug: " };
1041
1042         for( i_start = p_intf->p_sys->p_sub->i_start;
1043              i_start != i_stop;
1044              i_start = (i_start+1) % VLC_MSG_QSIZE )
1045         {
1046             NSString *o_msg;
1047             NSDictionary *o_attr;
1048             NSAttributedString *o_msg_color;
1049
1050             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
1051
1052             [o_msg_lock lock];
1053
1054             if( [o_msg_arr count] + 2 > 400 )
1055             {
1056                 unsigned rid[] = { 0, 1 };
1057                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
1058                            numIndices: sizeof(rid)/sizeof(rid[0])];
1059             }
1060
1061             o_attr = [NSDictionary dictionaryWithObject: o_gray
1062                 forKey: NSForegroundColorAttributeName];
1063             o_msg = [NSString stringWithFormat: @"%s%s",
1064                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1065                 ppsz_type[i_type]];
1066             o_msg_color = [[NSAttributedString alloc]
1067                 initWithString: o_msg attributes: o_attr];
1068             [o_msg_arr addObject: [o_msg_color autorelease]];
1069
1070             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
1071                 forKey: NSForegroundColorAttributeName];
1072             o_msg = [NSString stringWithFormat: @"%s\n",
1073                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1074             o_msg_color = [[NSAttributedString alloc]
1075                 initWithString: o_msg attributes: o_attr];
1076             [o_msg_arr addObject: [o_msg_color autorelease]];
1077
1078             [o_msg_lock unlock];
1079
1080             var_Get( p_intf->p_vlc, "verbose", &quiet );
1081
1082             if( i_type == 1 && quiet.i_int > -1 )
1083             {
1084                 NSString *o_my_msg = [NSString stringWithFormat: @"%s: %s\n",
1085                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1086                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1087
1088                 NSRange s_r = NSMakeRange( [[o_err_msg string] length], 0 );
1089                 [o_err_msg setEditable: YES];
1090                 [o_err_msg setSelectedRange: s_r];
1091                 [o_err_msg insertText: o_my_msg];
1092
1093                 [o_error makeKeyAndOrderFront: self];
1094                 [o_err_msg setEditable: NO];
1095             }
1096         }
1097
1098         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1099         p_intf->p_sys->p_sub->i_start = i_start;
1100         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1101     }
1102 }
1103
1104 - (void)playStatusUpdated:(int)i_status
1105 {
1106     if( i_status == PLAYING_S )
1107     {
1108         [o_btn_play setImage: o_img_pause];
1109         [o_btn_play setAlternateImage: o_img_pause_pressed];
1110         [o_btn_play setToolTip: _NS("Pause")];
1111         [o_mi_play setTitle: _NS("Pause")];
1112         [o_dmi_play setTitle: _NS("Pause")];
1113     }
1114     else
1115     {
1116         [o_btn_play setImage: o_img_play];
1117         [o_btn_play setAlternateImage: o_img_play_pressed];
1118         [o_btn_play setToolTip: _NS("Play")];
1119         [o_mi_play setTitle: _NS("Play")];
1120         [o_dmi_play setTitle: _NS("Play")];
1121     }
1122 }
1123
1124 - (void)setSubmenusEnabled:(BOOL)b_enabled
1125 {
1126     [o_mi_program setEnabled: b_enabled];
1127     [o_mi_title setEnabled: b_enabled];
1128     [o_mi_chapter setEnabled: b_enabled];
1129     [o_mi_audiotrack setEnabled: b_enabled];
1130     [o_mi_visual setEnabled: b_enabled];
1131     [o_mi_videotrack setEnabled: b_enabled];
1132     [o_mi_subtitle setEnabled: b_enabled];
1133     [o_mi_channels setEnabled: b_enabled];
1134     [o_mi_deinterlace setEnabled: b_enabled];
1135     [o_mi_ffmpeg_pp setEnabled: b_enabled];
1136     [o_mi_device setEnabled: b_enabled];
1137     [o_mi_screen setEnabled: b_enabled];
1138 }
1139
1140 - (void)manageVolumeSlider
1141 {
1142     audio_volume_t i_volume;
1143
1144     aout_VolumeGet( p_intf, &i_volume );
1145
1146     [o_volumeslider setFloatValue: (float)i_volume / AOUT_VOLUME_STEP];
1147     [o_volumeslider setEnabled: TRUE];
1148
1149     p_intf->p_sys->b_mute = ( i_volume == 0 );
1150 }
1151
1152 - (IBAction)timesliderUpdate:(id)sender
1153 {
1154 #define p_input p_intf->p_sys->p_input
1155     float f_updated;
1156
1157     switch( [[NSApp currentEvent] type] )
1158     {
1159         case NSLeftMouseUp:
1160         case NSLeftMouseDown:
1161         case NSLeftMouseDragged:
1162             f_updated = [sender floatValue];
1163             break;
1164
1165         default:
1166             return;
1167     }
1168
1169     if( p_input != NULL )
1170     {
1171         vlc_value_t time;
1172         vlc_value_t pos;
1173         mtime_t i_seconds;
1174         NSString * o_time;
1175
1176         pos.f_float = f_updated / 10000.;
1177         var_Set( p_input, "position", pos );
1178         [o_timeslider setFloatValue: f_updated];
1179
1180         var_Get( p_input, "time", &time );
1181         i_seconds = time.i_time / 1000000;
1182
1183         o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
1184                         (int) (i_seconds / (60 * 60)),
1185                         (int) (i_seconds / 60 % 60),
1186                         (int) (i_seconds % 60)];
1187         [o_timefield setStringValue: o_time];
1188     }
1189 #undef p_input
1190 }
1191
1192 - (void)terminate
1193 {
1194     playlist_t * p_playlist;
1195     vout_thread_t * p_vout;
1196
1197     /* Stop playback */
1198     if( ( p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1199                                         FIND_ANYWHERE ) ) )
1200     {
1201         playlist_Stop( p_playlist );
1202         vlc_object_release( p_playlist );
1203     }
1204
1205     /* FIXME - Wait here until all vouts are terminated because
1206        libvlc's VLC_CleanUp destroys interfaces before vouts, which isn't
1207        good on OS X. We definitly need a cleaner way to handle this,
1208        but this may hopefully be good enough for now.
1209          -- titer 2003/11/22 */
1210     while( ( p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1211                                        FIND_ANYWHERE ) ) )
1212     {
1213         vlc_object_release( p_vout );
1214         msleep( 100000 );
1215     }
1216     msleep( 500000 );
1217
1218     if( o_img_pause_pressed != nil )
1219     {
1220         [o_img_pause_pressed release];
1221         o_img_pause_pressed = nil;
1222     }
1223
1224     if( o_img_pause_pressed != nil )
1225     {
1226         [o_img_pause_pressed release];
1227         o_img_pause_pressed = nil;
1228     }
1229
1230     if( o_img_pause != nil )
1231     {
1232         [o_img_pause release];
1233         o_img_pause = nil;
1234     }
1235
1236     if( o_img_play != nil )
1237     {
1238         [o_img_play release];
1239         o_img_play = nil;
1240     }
1241
1242     if( o_msg_arr != nil )
1243     {
1244         [o_msg_arr removeAllObjects];
1245         [o_msg_arr release];
1246         o_msg_arr = nil;
1247     }
1248
1249     if( o_msg_lock != nil )
1250     {
1251         [o_msg_lock release];
1252         o_msg_lock = nil;
1253     }
1254
1255     /* write cached user defaults to disk */
1256     [[NSUserDefaults standardUserDefaults] synchronize];
1257
1258     p_intf->b_die = VLC_TRUE;
1259     [NSApp stop:NULL];
1260 }
1261
1262 - (IBAction)clearRecentItems:(id)sender
1263 {
1264     [[NSDocumentController sharedDocumentController]
1265                           clearRecentDocuments: nil];
1266 }
1267
1268 - (void)openRecentItem:(id)sender
1269 {
1270     [self application: nil openFile: [sender title]];
1271 }
1272
1273 - (IBAction)viewPreferences:(id)sender
1274 {
1275     [o_prefs showPrefs];
1276 }
1277
1278 - (IBAction)closeError:(id)sender
1279 {
1280     vlc_value_t val;
1281
1282     if( [o_err_ckbk_surpress state] == NSOnState )
1283     {
1284         val.i_int = -1;
1285         var_Set( p_intf->p_vlc, "verbose", val );
1286     }
1287     [o_err_msg setString: @""];
1288     [o_error performClose: self];
1289 }
1290
1291 - (IBAction)openReadMe:(id)sender
1292 {
1293     NSString * o_path = [[NSBundle mainBundle]
1294         pathForResource: @"README.MacOSX" ofType: @"rtf"];
1295
1296     [[NSWorkspace sharedWorkspace] openFile: o_path
1297                                    withApplication: @"TextEdit"];
1298 }
1299
1300 - (IBAction)openDocumentation:(id)sender
1301 {
1302     NSURL * o_url = [NSURL URLWithString:
1303         @"http://www.videolan.org/doc/"];
1304
1305     [[NSWorkspace sharedWorkspace] openURL: o_url];
1306 }
1307
1308 - (IBAction)reportABug:(id)sender
1309 {
1310     NSURL * o_url = [NSURL URLWithString:
1311         @"http://www.videolan.org/support/bug-reporting.html"];
1312
1313     [[NSWorkspace sharedWorkspace] openURL: o_url];
1314 }
1315
1316 - (IBAction)openWebsite:(id)sender
1317 {
1318     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
1319
1320     [[NSWorkspace sharedWorkspace] openURL: o_url];
1321 }
1322
1323 - (IBAction)openLicense:(id)sender
1324 {
1325     NSString * o_path = [[NSBundle mainBundle]
1326         pathForResource: @"COPYING" ofType: nil];
1327
1328     [[NSWorkspace sharedWorkspace] openFile: o_path
1329                                    withApplication: @"TextEdit"];
1330 }
1331
1332 - (IBAction)openCrashLog:(id)sender
1333 {
1334     NSString * o_path = [@"~/Library/Logs/CrashReporter/VLC.crash.log"
1335                                     stringByExpandingTildeInPath];
1336
1337
1338     if ( [[NSFileManager defaultManager] fileExistsAtPath: o_path ] )
1339     {
1340         [[NSWorkspace sharedWorkspace] openFile: o_path
1341                                     withApplication: @"Console"];
1342     }
1343     else
1344     {
1345         NSBeginInformationalAlertSheet(_NS("No CrashLog found"), @"Continue", nil, nil, o_msgs_panel, self, NULL, NULL, nil, _NS("Either you are running Mac OS X pre 10.2 or you haven't experienced any heavy crashes yet.") );
1346
1347     }
1348 }
1349
1350 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1351 {
1352     if( [o_notification object] == o_msgs_panel )
1353     {
1354         id o_msg;
1355         NSEnumerator * o_enum;
1356
1357         [o_messages setString: @""];
1358
1359         [o_msg_lock lock];
1360
1361         o_enum = [o_msg_arr objectEnumerator];
1362
1363         while( ( o_msg = [o_enum nextObject] ) != nil )
1364         {
1365             [o_messages insertText: o_msg];
1366         }
1367
1368         [o_msg_lock unlock];
1369     }
1370 }
1371
1372 @end
1373
1374 @implementation VLCMain (NSMenuValidation)
1375
1376 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1377 {
1378     NSString *o_title = [o_mi title];
1379     BOOL bEnabled = TRUE;
1380
1381     if( [o_title isEqualToString: _NS("License")] )
1382     {
1383         /* we need to do this only once */
1384         [self setupMenus];
1385     }
1386
1387     /* Recent Items Menu */
1388     if( [o_title isEqualToString: _NS("Clear Menu")] )
1389     {
1390         NSMenu * o_menu = [o_mi_open_recent submenu];
1391         int i_nb_items = [o_menu numberOfItems];
1392         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1393                                                        recentDocumentURLs];
1394         UInt32 i_nb_docs = [o_docs count];
1395
1396         if( i_nb_items > 1 )
1397         {
1398             while( --i_nb_items )
1399             {
1400                 [o_menu removeItemAtIndex: 0];
1401             }
1402         }
1403
1404         if( i_nb_docs > 0 )
1405         {
1406             NSURL * o_url;
1407             NSString * o_doc;
1408
1409             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1410
1411             while( TRUE )
1412             {
1413                 i_nb_docs--;
1414
1415                 o_url = [o_docs objectAtIndex: i_nb_docs];
1416
1417                 if( [o_url isFileURL] )
1418                 {
1419                     o_doc = [o_url path];
1420                 }
1421                 else
1422                 {
1423                     o_doc = [o_url absoluteString];
1424                 }
1425
1426                 [o_menu insertItemWithTitle: o_doc
1427                     action: @selector(openRecentItem:)
1428                     keyEquivalent: @"" atIndex: 0];
1429
1430                 if( i_nb_docs == 0 )
1431                 {
1432                     break;
1433                 }
1434             }
1435         }
1436         else
1437         {
1438             bEnabled = FALSE;
1439         }
1440     }
1441     return( bEnabled );
1442 }
1443
1444 @end
1445
1446 @implementation VLCMain (Internal)
1447
1448 - (void)handlePortMessage:(NSPortMessage *)o_msg
1449 {
1450     id ** val;
1451     NSData * o_data;
1452     NSValue * o_value;
1453     NSInvocation * o_inv;
1454     NSConditionLock * o_lock;
1455
1456     o_data = [[o_msg components] lastObject];
1457     o_inv = *((NSInvocation **)[o_data bytes]);
1458     [o_inv getArgument: &o_value atIndex: 2];
1459     val = (id **)[o_value pointerValue];
1460     [o_inv setArgument: val[1] atIndex: 2];
1461     o_lock = *(val[0]);
1462
1463     [o_lock lock];
1464     [o_inv invoke];
1465     [o_lock unlockWithCondition: 1];
1466 }
1467
1468 @end