]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* Fix display of playlist on OSX. Looks briljant :)
[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 #if 0
871             o_temp = [NSString stringWithUTF8String:
872                 p_playlist->p_input.psz_name];
873             if( o_temp == NULL )
874                 o_temp = [NSString stringWithCString:
875                     p_playlist->p_input.psz_name];
876             [o_scrollfield setStringValue: o_temp ];
877 #endif
878
879             p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
880                                                     FIND_ANYWHERE );
881             if( p_vout != NULL )
882             {
883                 id o_vout_wnd;
884                 NSEnumerator * o_enum = [[NSApp orderedWindows] objectEnumerator];
885
886                 while( ( o_vout_wnd = [o_enum nextObject] ) )
887                 {
888                     if( [[o_vout_wnd className] isEqualToString: @"VLCWindow"] )
889                     {
890                         ;[o_vout_wnd updateTitle];
891                     }
892                 }
893                 vlc_object_release( (vlc_object_t *)p_vout );
894             }
895             //[o_playlist updateRowSelection];
896             vlc_object_release( p_playlist );
897             p_intf->p_sys->b_current_title_update = FALSE;
898         }
899
900         if( p_input && [o_timeslider isEnabled] )
901         {
902             /* Update the slider */
903             vlc_value_t time;
904             NSString * o_time;
905             mtime_t i_seconds;
906             vlc_value_t pos;
907             float f_updated;
908
909             var_Get( p_input, "position", &pos );
910             f_updated = 10000. * pos.f_float;
911             [o_timeslider setFloatValue: f_updated];
912
913             var_Get( p_input, "time", &time );
914             i_seconds = time.i_time / 1000000;
915
916             o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
917                             (int) (i_seconds / (60 * 60)),
918                             (int) (i_seconds / 60 % 60),
919                             (int) (i_seconds % 60)];
920             [o_timefield setStringValue: o_time];
921         }
922
923         /* Manage volume status */
924         [self manageVolumeSlider];
925
926         /* Manage Playing status */
927         var_Get( p_input, "state", &val );
928         if( p_intf->p_sys->i_play_status != val.i_int )
929         {
930             p_intf->p_sys->i_play_status = val.i_int;
931             [self playStatusUpdated: p_intf->p_sys->i_play_status];
932         }
933     }
934     else
935     {
936         p_intf->p_sys->i_play_status = END_S;
937         p_intf->p_sys->b_intf_update = VLC_TRUE;
938         [self playStatusUpdated: p_intf->p_sys->i_play_status];
939         [self setSubmenusEnabled: FALSE];
940     }
941
942 #undef p_input
943
944     [self updateMessageArray];
945
946     [NSTimer scheduledTimerWithTimeInterval: 0.3
947         target: self selector: @selector(manageIntf:)
948         userInfo: nil repeats: FALSE];
949 }
950
951 - (void)setupMenus
952 {
953 #define p_input p_intf->p_sys->p_input
954     if( p_input != NULL )
955     {
956         [o_controls setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
957             var: "program" selector: @selector(toggleVar:)];
958
959         [o_controls setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
960             var: "title" selector: @selector(toggleVar:)];
961
962         [o_controls setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
963             var: "chapter" selector: @selector(toggleVar:)];
964
965         [o_controls setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
966             var: "audio-es" selector: @selector(toggleVar:)];
967
968         [o_controls setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
969             var: "video-es" selector: @selector(toggleVar:)];
970
971         [o_controls setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
972             var: "spu-es" selector: @selector(toggleVar:)];
973
974         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
975                                                     FIND_ANYWHERE );
976         if ( p_aout != NULL )
977         {
978             [o_controls setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
979                 var: "audio-channels" selector: @selector(toggleVar:)];
980
981             [o_controls setupVarMenuItem: o_mi_device target: (vlc_object_t *)p_aout
982                 var: "audio-device" selector: @selector(toggleVar:)];
983
984             [o_controls setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
985                 var: "visual" selector: @selector(toggleVar:)];
986             vlc_object_release( (vlc_object_t *)p_aout );
987         }
988
989         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
990                                                             FIND_ANYWHERE );
991
992         if ( p_vout != NULL )
993         {
994             vlc_object_t * p_dec_obj;
995
996             [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
997                 var: "video-device" selector: @selector(toggleVar:)];
998
999             [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
1000                 var: "deinterlace" selector: @selector(toggleVar:)];
1001
1002             p_dec_obj = (vlc_object_t *)vlc_object_find(
1003                                                  (vlc_object_t *)p_vout,
1004                                                  VLC_OBJECT_DECODER,
1005                                                  FIND_PARENT );
1006             if ( p_dec_obj != NULL )
1007             {
1008                [o_controls setupVarMenuItem: o_mi_ffmpeg_pp target:
1009                     (vlc_object_t *)p_dec_obj var:"ffmpeg-pp-q" selector:
1010                     @selector(toggleVar:)];
1011
1012                 vlc_object_release(p_dec_obj);
1013             }
1014             vlc_object_release( (vlc_object_t *)p_vout );
1015         }
1016     }
1017 #undef p_input
1018 }
1019
1020 - (void)updateMessageArray
1021 {
1022     int i_start, i_stop;
1023     vlc_value_t quiet;
1024
1025     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1026     i_stop = *p_intf->p_sys->p_sub->pi_stop;
1027     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1028
1029     if( p_intf->p_sys->p_sub->i_start != i_stop )
1030     {
1031         NSColor *o_white = [NSColor whiteColor];
1032         NSColor *o_red = [NSColor redColor];
1033         NSColor *o_yellow = [NSColor yellowColor];
1034         NSColor *o_gray = [NSColor grayColor];
1035
1036         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1037         static const char * ppsz_type[4] = { ": ", " error: ",
1038                                              " warning: ", " debug: " };
1039
1040         for( i_start = p_intf->p_sys->p_sub->i_start;
1041              i_start != i_stop;
1042              i_start = (i_start+1) % VLC_MSG_QSIZE )
1043         {
1044             NSString *o_msg;
1045             NSDictionary *o_attr;
1046             NSAttributedString *o_msg_color;
1047
1048             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
1049
1050             [o_msg_lock lock];
1051
1052             if( [o_msg_arr count] + 2 > 400 )
1053             {
1054                 unsigned rid[] = { 0, 1 };
1055                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
1056                            numIndices: sizeof(rid)/sizeof(rid[0])];
1057             }
1058
1059             o_attr = [NSDictionary dictionaryWithObject: o_gray
1060                 forKey: NSForegroundColorAttributeName];
1061             o_msg = [NSString stringWithFormat: @"%s%s",
1062                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1063                 ppsz_type[i_type]];
1064             o_msg_color = [[NSAttributedString alloc]
1065                 initWithString: o_msg attributes: o_attr];
1066             [o_msg_arr addObject: [o_msg_color autorelease]];
1067
1068             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
1069                 forKey: NSForegroundColorAttributeName];
1070             o_msg = [NSString stringWithFormat: @"%s\n",
1071                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1072             o_msg_color = [[NSAttributedString alloc]
1073                 initWithString: o_msg attributes: o_attr];
1074             [o_msg_arr addObject: [o_msg_color autorelease]];
1075
1076             [o_msg_lock unlock];
1077
1078             var_Get( p_intf->p_vlc, "verbose", &quiet );
1079
1080             if( i_type == 1 && quiet.i_int > -1 )
1081             {
1082                 NSString *o_my_msg = [NSString stringWithFormat: @"%s: %s\n",
1083                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1084                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1085
1086                 NSRange s_r = NSMakeRange( [[o_err_msg string] length], 0 );
1087                 [o_err_msg setEditable: YES];
1088                 [o_err_msg setSelectedRange: s_r];
1089                 [o_err_msg insertText: o_my_msg];
1090
1091                 [o_error makeKeyAndOrderFront: self];
1092                 [o_err_msg setEditable: NO];
1093             }
1094         }
1095
1096         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1097         p_intf->p_sys->p_sub->i_start = i_start;
1098         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1099     }
1100 }
1101
1102 - (void)playStatusUpdated:(int)i_status
1103 {
1104     if( i_status == PLAYING_S )
1105     {
1106         [o_btn_play setImage: o_img_pause];
1107         [o_btn_play setAlternateImage: o_img_pause_pressed];
1108         [o_btn_play setToolTip: _NS("Pause")];
1109         [o_mi_play setTitle: _NS("Pause")];
1110         [o_dmi_play setTitle: _NS("Pause")];
1111     }
1112     else
1113     {
1114         [o_btn_play setImage: o_img_play];
1115         [o_btn_play setAlternateImage: o_img_play_pressed];
1116         [o_btn_play setToolTip: _NS("Play")];
1117         [o_mi_play setTitle: _NS("Play")];
1118         [o_dmi_play setTitle: _NS("Play")];
1119     }
1120 }
1121
1122 - (void)setSubmenusEnabled:(BOOL)b_enabled
1123 {
1124     [o_mi_program setEnabled: b_enabled];
1125     [o_mi_title setEnabled: b_enabled];
1126     [o_mi_chapter setEnabled: b_enabled];
1127     [o_mi_audiotrack setEnabled: b_enabled];
1128     [o_mi_visual setEnabled: b_enabled];
1129     [o_mi_videotrack setEnabled: b_enabled];
1130     [o_mi_subtitle setEnabled: b_enabled];
1131     [o_mi_channels setEnabled: b_enabled];
1132     [o_mi_deinterlace setEnabled: b_enabled];
1133     [o_mi_ffmpeg_pp setEnabled: b_enabled];
1134     [o_mi_device setEnabled: b_enabled];
1135     [o_mi_screen setEnabled: b_enabled];
1136 }
1137
1138 - (void)manageVolumeSlider
1139 {
1140     audio_volume_t i_volume;
1141
1142     aout_VolumeGet( p_intf, &i_volume );
1143
1144     [o_volumeslider setFloatValue: (float)i_volume / AOUT_VOLUME_STEP];
1145     [o_volumeslider setEnabled: TRUE];
1146
1147     p_intf->p_sys->b_mute = ( i_volume == 0 );
1148 }
1149
1150 - (IBAction)timesliderUpdate:(id)sender
1151 {
1152 #define p_input p_intf->p_sys->p_input
1153     float f_updated;
1154
1155     switch( [[NSApp currentEvent] type] )
1156     {
1157         case NSLeftMouseUp:
1158         case NSLeftMouseDown:
1159         case NSLeftMouseDragged:
1160             f_updated = [sender floatValue];
1161             break;
1162
1163         default:
1164             return;
1165     }
1166
1167     if( p_input != NULL )
1168     {
1169         vlc_value_t time;
1170         vlc_value_t pos;
1171         mtime_t i_seconds;
1172         NSString * o_time;
1173
1174         pos.f_float = f_updated / 10000.;
1175         var_Set( p_input, "position", pos );
1176         [o_timeslider setFloatValue: f_updated];
1177
1178         var_Get( p_input, "time", &time );
1179         i_seconds = time.i_time / 1000000;
1180
1181         o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
1182                         (int) (i_seconds / (60 * 60)),
1183                         (int) (i_seconds / 60 % 60),
1184                         (int) (i_seconds % 60)];
1185         [o_timefield setStringValue: o_time];
1186     }
1187 #undef p_input
1188 }
1189
1190 - (void)terminate
1191 {
1192     playlist_t * p_playlist;
1193     vout_thread_t * p_vout;
1194
1195     /* Stop playback */
1196     if( ( p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1197                                         FIND_ANYWHERE ) ) )
1198     {
1199         playlist_Stop( p_playlist );
1200         vlc_object_release( p_playlist );
1201     }
1202
1203     /* FIXME - Wait here until all vouts are terminated because
1204        libvlc's VLC_CleanUp destroys interfaces before vouts, which isn't
1205        good on OS X. We definitly need a cleaner way to handle this,
1206        but this may hopefully be good enough for now.
1207          -- titer 2003/11/22 */
1208     while( ( p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1209                                        FIND_ANYWHERE ) ) )
1210     {
1211         vlc_object_release( p_vout );
1212         msleep( 100000 );
1213     }
1214     msleep( 500000 );
1215
1216     if( o_img_pause_pressed != nil )
1217     {
1218         [o_img_pause_pressed release];
1219         o_img_pause_pressed = nil;
1220     }
1221
1222     if( o_img_pause_pressed != nil )
1223     {
1224         [o_img_pause_pressed release];
1225         o_img_pause_pressed = nil;
1226     }
1227
1228     if( o_img_pause != nil )
1229     {
1230         [o_img_pause release];
1231         o_img_pause = nil;
1232     }
1233
1234     if( o_img_play != nil )
1235     {
1236         [o_img_play release];
1237         o_img_play = nil;
1238     }
1239
1240     if( o_msg_arr != nil )
1241     {
1242         [o_msg_arr removeAllObjects];
1243         [o_msg_arr release];
1244         o_msg_arr = nil;
1245     }
1246
1247     if( o_msg_lock != nil )
1248     {
1249         [o_msg_lock release];
1250         o_msg_lock = nil;
1251     }
1252
1253     /* write cached user defaults to disk */
1254     [[NSUserDefaults standardUserDefaults] synchronize];
1255
1256     p_intf->b_die = VLC_TRUE;
1257     [NSApp stop:NULL];
1258 }
1259
1260 - (IBAction)clearRecentItems:(id)sender
1261 {
1262     [[NSDocumentController sharedDocumentController]
1263                           clearRecentDocuments: nil];
1264 }
1265
1266 - (void)openRecentItem:(id)sender
1267 {
1268     [self application: nil openFile: [sender title]];
1269 }
1270
1271 - (IBAction)viewPreferences:(id)sender
1272 {
1273     [o_prefs showPrefs];
1274 }
1275
1276 - (IBAction)closeError:(id)sender
1277 {
1278     vlc_value_t val;
1279
1280     if( [o_err_ckbk_surpress state] == NSOnState )
1281     {
1282         val.i_int = -1;
1283         var_Set( p_intf->p_vlc, "verbose", val );
1284     }
1285     [o_err_msg setString: @""];
1286     [o_error performClose: self];
1287 }
1288
1289 - (IBAction)openReadMe:(id)sender
1290 {
1291     NSString * o_path = [[NSBundle mainBundle]
1292         pathForResource: @"README.MacOSX" ofType: @"rtf"];
1293
1294     [[NSWorkspace sharedWorkspace] openFile: o_path
1295                                    withApplication: @"TextEdit"];
1296 }
1297
1298 - (IBAction)openDocumentation:(id)sender
1299 {
1300     NSURL * o_url = [NSURL URLWithString:
1301         @"http://www.videolan.org/doc/"];
1302
1303     [[NSWorkspace sharedWorkspace] openURL: o_url];
1304 }
1305
1306 - (IBAction)reportABug:(id)sender
1307 {
1308     NSURL * o_url = [NSURL URLWithString:
1309         @"http://www.videolan.org/support/bug-reporting.html"];
1310
1311     [[NSWorkspace sharedWorkspace] openURL: o_url];
1312 }
1313
1314 - (IBAction)openWebsite:(id)sender
1315 {
1316     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
1317
1318     [[NSWorkspace sharedWorkspace] openURL: o_url];
1319 }
1320
1321 - (IBAction)openLicense:(id)sender
1322 {
1323     NSString * o_path = [[NSBundle mainBundle]
1324         pathForResource: @"COPYING" ofType: nil];
1325
1326     [[NSWorkspace sharedWorkspace] openFile: o_path
1327                                    withApplication: @"TextEdit"];
1328 }
1329
1330 - (IBAction)openCrashLog:(id)sender
1331 {
1332     NSString * o_path = [@"~/Library/Logs/CrashReporter/VLC.crash.log"
1333                                     stringByExpandingTildeInPath];
1334
1335
1336     if ( [[NSFileManager defaultManager] fileExistsAtPath: o_path ] )
1337     {
1338         [[NSWorkspace sharedWorkspace] openFile: o_path
1339                                     withApplication: @"Console"];
1340     }
1341     else
1342     {
1343         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.") );
1344
1345     }
1346 }
1347
1348 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1349 {
1350     if( [o_notification object] == o_msgs_panel )
1351     {
1352         id o_msg;
1353         NSEnumerator * o_enum;
1354
1355         [o_messages setString: @""];
1356
1357         [o_msg_lock lock];
1358
1359         o_enum = [o_msg_arr objectEnumerator];
1360
1361         while( ( o_msg = [o_enum nextObject] ) != nil )
1362         {
1363             [o_messages insertText: o_msg];
1364         }
1365
1366         [o_msg_lock unlock];
1367     }
1368 }
1369
1370 @end
1371
1372 @implementation VLCMain (NSMenuValidation)
1373
1374 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1375 {
1376     NSString *o_title = [o_mi title];
1377     BOOL bEnabled = TRUE;
1378
1379     if( [o_title isEqualToString: _NS("License")] )
1380     {
1381         /* we need to do this only once */
1382         [self setupMenus];
1383     }
1384
1385     /* Recent Items Menu */
1386     if( [o_title isEqualToString: _NS("Clear Menu")] )
1387     {
1388         NSMenu * o_menu = [o_mi_open_recent submenu];
1389         int i_nb_items = [o_menu numberOfItems];
1390         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1391                                                        recentDocumentURLs];
1392         UInt32 i_nb_docs = [o_docs count];
1393
1394         if( i_nb_items > 1 )
1395         {
1396             while( --i_nb_items )
1397             {
1398                 [o_menu removeItemAtIndex: 0];
1399             }
1400         }
1401
1402         if( i_nb_docs > 0 )
1403         {
1404             NSURL * o_url;
1405             NSString * o_doc;
1406
1407             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1408
1409             while( TRUE )
1410             {
1411                 i_nb_docs--;
1412
1413                 o_url = [o_docs objectAtIndex: i_nb_docs];
1414
1415                 if( [o_url isFileURL] )
1416                 {
1417                     o_doc = [o_url path];
1418                 }
1419                 else
1420                 {
1421                     o_doc = [o_url absoluteString];
1422                 }
1423
1424                 [o_menu insertItemWithTitle: o_doc
1425                     action: @selector(openRecentItem:)
1426                     keyEquivalent: @"" atIndex: 0];
1427
1428                 if( i_nb_docs == 0 )
1429                 {
1430                     break;
1431                 }
1432             }
1433         }
1434         else
1435         {
1436             bEnabled = FALSE;
1437         }
1438     }
1439     return( bEnabled );
1440 }
1441
1442 @end
1443
1444 @implementation VLCMain (Internal)
1445
1446 - (void)handlePortMessage:(NSPortMessage *)o_msg
1447 {
1448     id ** val;
1449     NSData * o_data;
1450     NSValue * o_value;
1451     NSInvocation * o_inv;
1452     NSConditionLock * o_lock;
1453
1454     o_data = [[o_msg components] lastObject];
1455     o_inv = *((NSInvocation **)[o_data bytes]);
1456     [o_inv getArgument: &o_value atIndex: 2];
1457     val = (id **)[o_value pointerValue];
1458     [o_inv setArgument: val[1] atIndex: 2];
1459     o_lock = *(val[0]);
1460
1461     [o_lock lock];
1462     [o_inv invoke];
1463     [o_lock unlockWithCondition: 1];
1464 }
1465
1466 @end