]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
cc9e77f46bdb814c7aca4efcad7058bf2f9f0b2c
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2008 the VideoLAN team
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  *          Felix Paul Kühne <fkuehne at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <sys/param.h>                                    /* for MAXPATHLEN */
32 #include <string.h>
33 #include <vlc_keys.h>
34
35 #ifdef HAVE_CONFIG_H
36 #   include "config.h"
37 #endif
38
39 #import "intf.h"
40 #import "fspanel.h"
41 #import "vout.h"
42 #import "prefs.h"
43 #import "playlist.h"
44 #import "playlistinfo.h"
45 #import "controls.h"
46 #import "about.h"
47 #import "open.h"
48 #import "wizard.h"
49 #import "extended.h"
50 #import "bookmarks.h"
51 #import "sfilters.h"
52 #import "interaction.h"
53 #import "embeddedwindow.h"
54 #import "update.h"
55 #import "AppleRemote.h"
56 #import "eyetv.h"
57 #import "simple_prefs.h"
58
59 #import <vlc_input.h>
60 #import <vlc_interface.h>
61
62 /*****************************************************************************
63  * Local prototypes.
64  *****************************************************************************/
65 static void Run ( intf_thread_t *p_intf );
66
67 /* Quick hack */
68 /*****************************************************************************
69  * VLCApplication implementation (this hack is really disgusting now,
70  *                                feel free to fix.)
71  *****************************************************************************/
72 @interface VLCApplication : NSApplication
73 {
74    libvlc_int_t *o_libvlc;
75 }
76 - (void)setVLC: (libvlc_int_t *)p_libvlc;
77 @end
78
79
80 @implementation VLCApplication
81 - (void)setVLC: (libvlc_int_t *) p_libvlc
82 {
83     o_libvlc = p_libvlc;
84 }
85 - (void)terminate: (id)sender
86 {
87     vlc_object_kill( o_libvlc );
88     [super terminate: sender];
89 }
90 @end
91
92 /*****************************************************************************
93  * OpenIntf: initialize interface
94  *****************************************************************************/
95 int OpenIntf ( vlc_object_t *p_this )
96 {
97     intf_thread_t *p_intf = (intf_thread_t*) p_this;
98
99     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
100     if( p_intf->p_sys == NULL )
101     {
102         return( 1 );
103     }
104
105     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
106
107     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
108
109     p_intf->p_sys->o_sendport = [[NSPort port] retain];
110     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
111     p_intf->pf_run = Run;
112     p_intf->b_should_run_on_first_thread = true;
113
114     return( 0 );
115 }
116
117 /*****************************************************************************
118  * CloseIntf: destroy interface
119  *****************************************************************************/
120 void CloseIntf ( vlc_object_t *p_this )
121 {
122     intf_thread_t *p_intf = (intf_thread_t*) p_this;
123
124     [[VLCMain sharedInstance] setIntf: nil];
125     
126     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
127
128     [p_intf->p_sys->o_sendport release];
129     [p_intf->p_sys->o_pool release];
130
131     free( p_intf->p_sys );
132 }
133
134 /*****************************************************************************
135  * KillerThread: Thread that kill the application
136  *****************************************************************************/
137 static void * KillerThread( void *user_data )
138 {
139     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
140
141     intf_thread_t *p_intf = user_data;
142     
143     vlc_object_lock ( p_intf );
144     while( vlc_object_alive( p_intf ) )
145         vlc_object_wait( p_intf );
146     vlc_object_unlock( p_intf );
147
148     msg_Dbg( p_intf, "Killing the Mac OS X module" );
149
150     /* We are dead, terminate */
151     [NSApp terminate: nil];
152     [o_pool release];
153     return NULL;
154 }
155
156 /*****************************************************************************
157  * Run: main loop
158  *****************************************************************************/
159 jmp_buf jmpbuffer;
160
161 static void Run( intf_thread_t *p_intf )
162 {
163     sigset_t set;
164
165     /* Do it again - for some unknown reason, vlc_thread_create() often
166      * fails to go to real-time priority with the first launched thread
167      * (???) --Meuuh */
168     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
169
170     /* Make sure the "force quit" menu item does quit instantly.
171      * VLC overrides SIGTERM which is sent by the "force quit"
172      * menu item to make sure deamon mode quits gracefully, so
173      * we un-override SIGTERM here. */
174     sigemptyset( &set );
175     sigaddset( &set, SIGTERM );
176     pthread_sigmask( SIG_UNBLOCK, &set, NULL );
177
178     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
179
180     /* Install a jmpbuffer to where we can go back before the NSApp exit
181      * see applicationWillTerminate: */
182     /* We need that code to run on main thread */
183     [VLCApplication sharedApplication];
184     [NSApp setVLC: p_intf->p_libvlc];
185
186     [[VLCMain sharedInstance] setIntf: p_intf];
187     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
188
189     /* Setup a thread that will monitor the module killing */
190     pthread_t killer_thread;
191     pthread_create( &killer_thread, NULL, KillerThread, p_intf );
192
193     /* Install a jmpbuffer to where we can go back before the NSApp exit
194      * see applicationWillTerminate: */
195     if(setjmp(jmpbuffer) == 0)
196         [NSApp run];
197
198     pthread_join( killer_thread, NULL );
199
200     [o_pool release];
201 }
202
203 int ExecuteOnMainThread( id target, SEL sel, void * p_arg )
204 {
205     int i_ret = 0;
206
207     //NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
208
209     if( [target respondsToSelector: @selector(performSelectorOnMainThread:
210                                              withObject:waitUntilDone:)] )
211     {
212         [target performSelectorOnMainThread: sel
213                 withObject: [NSValue valueWithPointer: p_arg]
214                 waitUntilDone: NO];
215     }
216     else if( NSApp != nil && [[VLCMain sharedInstance] respondsToSelector: @selector(getIntf)] )
217     {
218         NSValue * o_v1;
219         NSValue * o_v2;
220         NSArray * o_array;
221         NSPort * o_recv_port;
222         NSInvocation * o_inv;
223         NSPortMessage * o_msg;
224         intf_thread_t * p_intf;
225         NSConditionLock * o_lock;
226         NSMethodSignature * o_sig;
227
228         id * val[] = { &o_lock, &o_v2 };
229
230         p_intf = (intf_thread_t *)VLCIntf;
231
232         o_recv_port = [[NSPort port] retain];
233         o_v1 = [NSValue valueWithPointer: val];
234         o_v2 = [NSValue valueWithPointer: p_arg];
235
236         o_sig = [target methodSignatureForSelector: sel];
237         o_inv = [NSInvocation invocationWithMethodSignature: o_sig];
238         [o_inv setArgument: &o_v1 atIndex: 2];
239         [o_inv setTarget: target];
240         [o_inv setSelector: sel];
241
242         o_array = [NSArray arrayWithObject:
243             [NSData dataWithBytes: &o_inv length: sizeof(o_inv)]];
244         o_msg = [[NSPortMessage alloc]
245             initWithSendPort: p_intf->p_sys->o_sendport
246             receivePort: o_recv_port components: o_array];
247
248         o_lock = [[NSConditionLock alloc] initWithCondition: 0];
249         [o_msg sendBeforeDate: [NSDate distantPast]];
250         [o_lock lockWhenCondition: 1];
251         [o_lock unlock];
252         [o_lock release];
253
254         [o_msg release];
255         [o_recv_port release];
256     }
257     else
258     {
259         i_ret = 1;
260     }
261
262     //[o_pool release];
263
264     return( i_ret );
265 }
266
267 /*****************************************************************************
268  * playlistChanged: Callback triggered by the intf-change playlist
269  * variable, to let the intf update the playlist.
270  *****************************************************************************/
271 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
272                      vlc_value_t old_val, vlc_value_t new_val, void *param )
273 {
274     intf_thread_t * p_intf = VLCIntf;
275     p_intf->p_sys->b_playlist_update = true;
276     p_intf->p_sys->b_intf_update = true;
277     p_intf->p_sys->b_playmode_update = true;
278     p_intf->p_sys->b_current_title_update = true;
279     return VLC_SUCCESS;
280 }
281
282 /*****************************************************************************
283  * ShowController: Callback triggered by the show-intf playlist variable
284  * through the ShowIntf-control-intf, to let us show the controller-win;
285  * usually when in fullscreen-mode
286  *****************************************************************************/
287 static int ShowController( vlc_object_t *p_this, const char *psz_variable,
288                      vlc_value_t old_val, vlc_value_t new_val, void *param )
289 {
290     intf_thread_t * p_intf = VLCIntf;
291     p_intf->p_sys->b_intf_show = true;
292     return VLC_SUCCESS;
293 }
294
295 /*****************************************************************************
296  * FullscreenChanged: Callback triggered by the fullscreen-change playlist
297  * variable, to let the intf update the controller.
298  *****************************************************************************/
299 static int FullscreenChanged( vlc_object_t *p_this, const char *psz_variable,
300                      vlc_value_t old_val, vlc_value_t new_val, void *param )
301 {
302     intf_thread_t * p_intf = VLCIntf;
303     p_intf->p_sys->b_fullscreen_update = true;
304     return VLC_SUCCESS;
305 }
306
307 /*****************************************************************************
308  * InteractCallback: Callback triggered by the interaction
309  * variable, to let the intf display error and interaction dialogs
310  *****************************************************************************/
311 static int InteractCallback( vlc_object_t *p_this, const char *psz_variable,
312                      vlc_value_t old_val, vlc_value_t new_val, void *param )
313 {
314     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
315     VLCMain *interface = (VLCMain *)param;
316     interaction_dialog_t *p_dialog = (interaction_dialog_t *)(new_val.p_address);
317     NSValue *o_value = [NSValue valueWithPointer:p_dialog];
318  
319     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCNewInteractionEventNotification" object:[interface getInteractionList]
320      userInfo:[NSDictionary dictionaryWithObject:o_value forKey:@"VLCDialogPointer"]];
321  
322     [o_pool release];
323     return VLC_SUCCESS;
324 }
325
326 static struct
327 {
328     unichar i_nskey;
329     unsigned int i_vlckey;
330 } nskeys_to_vlckeys[] =
331 {
332     { NSUpArrowFunctionKey, KEY_UP },
333     { NSDownArrowFunctionKey, KEY_DOWN },
334     { NSLeftArrowFunctionKey, KEY_LEFT },
335     { NSRightArrowFunctionKey, KEY_RIGHT },
336     { NSF1FunctionKey, KEY_F1 },
337     { NSF2FunctionKey, KEY_F2 },
338     { NSF3FunctionKey, KEY_F3 },
339     { NSF4FunctionKey, KEY_F4 },
340     { NSF5FunctionKey, KEY_F5 },
341     { NSF6FunctionKey, KEY_F6 },
342     { NSF7FunctionKey, KEY_F7 },
343     { NSF8FunctionKey, KEY_F8 },
344     { NSF9FunctionKey, KEY_F9 },
345     { NSF10FunctionKey, KEY_F10 },
346     { NSF11FunctionKey, KEY_F11 },
347     { NSF12FunctionKey, KEY_F12 },
348     { NSHomeFunctionKey, KEY_HOME },
349     { NSEndFunctionKey, KEY_END },
350     { NSPageUpFunctionKey, KEY_PAGEUP },
351     { NSPageDownFunctionKey, KEY_PAGEDOWN },
352     { NSTabCharacter, KEY_TAB },
353     { NSCarriageReturnCharacter, KEY_ENTER },
354     { NSEnterCharacter, KEY_ENTER },
355     { NSBackspaceCharacter, KEY_BACKSPACE },
356     { (unichar) ' ', KEY_SPACE },
357     { (unichar) 0x1b, KEY_ESC },
358     {0,0}
359 };
360
361 unichar VLCKeyToCocoa( unsigned int i_key )
362 {
363     unsigned int i;
364
365     for( i = 0; nskeys_to_vlckeys[i].i_vlckey != 0; i++ )
366     {
367         if( nskeys_to_vlckeys[i].i_vlckey == (i_key & ~KEY_MODIFIER) )
368         {
369             return nskeys_to_vlckeys[i].i_nskey;
370         }
371     }
372     return (unichar)(i_key & ~KEY_MODIFIER);
373 }
374
375 unsigned int CocoaKeyToVLC( unichar i_key )
376 {
377     unsigned int i;
378
379     for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
380     {
381         if( nskeys_to_vlckeys[i].i_nskey == i_key )
382         {
383             return nskeys_to_vlckeys[i].i_vlckey;
384         }
385     }
386     return (unsigned int)i_key;
387 }
388
389 unsigned int VLCModifiersToCocoa( unsigned int i_key )
390 {
391     unsigned int new = 0;
392     if( i_key & KEY_MODIFIER_COMMAND )
393         new |= NSCommandKeyMask;
394     if( i_key & KEY_MODIFIER_ALT )
395         new |= NSAlternateKeyMask;
396     if( i_key & KEY_MODIFIER_SHIFT )
397         new |= NSShiftKeyMask;
398     if( i_key & KEY_MODIFIER_CTRL )
399         new |= NSControlKeyMask;
400     return new;
401 }
402
403 /*****************************************************************************
404  * VLCMain implementation
405  *****************************************************************************/
406 @implementation VLCMain
407
408 static VLCMain *_o_sharedMainInstance = nil;
409
410 + (VLCMain *)sharedInstance
411 {
412     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
413 }
414
415 - (id)init
416 {
417     if( _o_sharedMainInstance) 
418     {
419         [self dealloc];
420         return _o_sharedMainInstance;
421     } 
422     else
423         _o_sharedMainInstance = [super init];
424
425     o_about = [[VLAboutBox alloc] init];
426     o_prefs = [[VLCPrefs alloc] init];
427     o_open = [[VLCOpen alloc] init];
428     o_wizard = [[VLCWizard alloc] init];
429     o_extended = nil;
430     o_bookmarks = [[VLCBookmarks alloc] init];
431     o_embedded_list = [[VLCEmbeddedList alloc] init];
432     o_interaction_list = [[VLCInteractionList alloc] init];
433     o_info = [[VLCInfo alloc] init];
434     o_sfilters = nil;
435 #ifdef UPDATE_CHECK
436     o_update = [[VLCUpdate alloc] init];
437 #endif
438
439     i_lastShownVolume = -1;
440
441     o_remote = [[AppleRemote alloc] init];
442     [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
443     [o_remote setDelegate: _o_sharedMainInstance];
444
445     o_eyetv = [[VLCEyeTVController alloc] init];
446
447     /* announce our launch to a potential eyetv plugin */
448     [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
449                                                                    object: @"VLCEyeTVSupport"
450                                                                  userInfo: NULL
451                                                        deliverImmediately: YES];
452
453     return _o_sharedMainInstance;
454 }
455
456 - (void)setIntf: (intf_thread_t *)p_mainintf {
457     p_intf = p_mainintf;
458 }
459
460 - (intf_thread_t *)getIntf {
461     return p_intf;
462 }
463
464 - (void)awakeFromNib
465 {
466     unsigned int i_key = 0;
467     playlist_t *p_playlist;
468     vlc_value_t val;
469
470     /* Check if we already did this once. Opening the other nibs calls it too, because VLCMain is the owner */
471     if( nib_main_loaded ) return;
472
473     [self initStrings];
474     [o_window setExcludedFromWindowsMenu: TRUE];
475     [o_msgs_panel setExcludedFromWindowsMenu: TRUE];
476     [o_msgs_panel setDelegate: self];
477
478     i_key = config_GetInt( p_intf, "key-quit" );
479     [o_mi_quit setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
480     [o_mi_quit setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
481     i_key = config_GetInt( p_intf, "key-play-pause" );
482     [o_mi_play setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
483     [o_mi_play setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
484     i_key = config_GetInt( p_intf, "key-stop" );
485     [o_mi_stop setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
486     [o_mi_stop setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
487     i_key = config_GetInt( p_intf, "key-faster" );
488     [o_mi_faster setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
489     [o_mi_faster setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
490     i_key = config_GetInt( p_intf, "key-slower" );
491     [o_mi_slower setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
492     [o_mi_slower setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
493     i_key = config_GetInt( p_intf, "key-prev" );
494     [o_mi_previous setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
495     [o_mi_previous setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
496     i_key = config_GetInt( p_intf, "key-next" );
497     [o_mi_next setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
498     [o_mi_next setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
499     i_key = config_GetInt( p_intf, "key-jump+short" );
500     [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
501     [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
502     i_key = config_GetInt( p_intf, "key-jump-short" );
503     [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
504     [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
505     i_key = config_GetInt( p_intf, "key-jump+medium" );
506     [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
507     [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
508     i_key = config_GetInt( p_intf, "key-jump-medium" );
509     [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
510     [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
511     i_key = config_GetInt( p_intf, "key-jump+long" );
512     [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
513     [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
514     i_key = config_GetInt( p_intf, "key-jump-long" );
515     [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
516     [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
517     i_key = config_GetInt( p_intf, "key-vol-up" );
518     [o_mi_vol_up setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
519     [o_mi_vol_up setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
520     i_key = config_GetInt( p_intf, "key-vol-down" );
521     [o_mi_vol_down setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
522     [o_mi_vol_down setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
523     i_key = config_GetInt( p_intf, "key-vol-mute" );
524     [o_mi_mute setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
525     [o_mi_mute setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
526     i_key = config_GetInt( p_intf, "key-fullscreen" );
527     [o_mi_fullscreen setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
528     [o_mi_fullscreen setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
529     i_key = config_GetInt( p_intf, "key-snapshot" );
530     [o_mi_snapshot setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
531     [o_mi_snapshot setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
532
533     var_Create( p_intf, "intf-change", VLC_VAR_BOOL );
534
535     [self setSubmenusEnabled: FALSE];
536     [self manageVolumeSlider];
537     [o_window setDelegate: self];
538  
539     b_restore_size = false;
540     if( [o_window frame].size.height <= 200 )
541     {
542         b_small_window = YES;
543         [o_window setFrame: NSMakeRect( [o_window frame].origin.x,
544             [o_window frame].origin.y, [o_window frame].size.width,
545             [o_window minSize].height ) display: YES animate:YES];
546         [o_playlist_view setAutoresizesSubviews: NO];
547     }
548     else
549     {
550         b_small_window = NO;
551         [o_playlist_view setFrame: NSMakeRect( 10, 10, [o_window frame].size.width - 20, [o_window frame].size.height - 105 )];
552         [o_playlist_view setNeedsDisplay:YES];
553         [o_playlist_view setAutoresizesSubviews: YES];
554         [[o_window contentView] addSubview: o_playlist_view];
555     }
556     [self updateTogglePlaylistState];
557
558     o_size_with_playlist = [o_window frame].size;
559
560     p_playlist = pl_Yield( p_intf );
561
562     var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
563     val.b_bool = false;
564
565     var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
566     var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
567
568     vlc_object_release( p_playlist );
569  
570     var_Create( p_intf, "interaction", VLC_VAR_ADDRESS );
571     var_AddCallback( p_intf, "interaction", InteractCallback, self );
572     p_intf->b_interaction = true;
573
574     /* update the playmode stuff */
575     p_intf->p_sys->b_playmode_update = true;
576
577     [[NSNotificationCenter defaultCenter] addObserver: self
578                                              selector: @selector(refreshVoutDeviceMenu:)
579                                                  name: NSApplicationDidChangeScreenParametersNotification
580                                                object: nil];
581
582     o_img_play = [NSImage imageNamed: @"play"];
583     o_img_pause = [NSImage imageNamed: @"pause"];    
584     
585     [self controlTintChanged];
586
587     [[NSNotificationCenter defaultCenter] addObserver: self
588                                              selector: @selector( controlTintChanged )
589                                                  name: NSControlTintDidChangeNotification
590                                                object: nil];
591     
592     nib_main_loaded = TRUE;
593 }
594
595 - (void)controlTintChanged
596 {
597     BOOL b_playing = NO;
598     
599     if( [o_btn_play alternateImage] == o_img_play_pressed )
600         b_playing = YES;
601     
602     if( [NSColor currentControlTint] == NSGraphiteControlTint )
603     {
604         o_img_play_pressed = [NSImage imageNamed: @"play_graphite"];
605         o_img_pause_pressed = [NSImage imageNamed: @"pause_graphite"];
606         
607         [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_graphite"]];
608         [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_graphite"]];
609         [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_graphite"]];
610         [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_graphite"]];
611         [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_graphite"]];
612         [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_graphite"]];
613         [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_graphite"]];
614         [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_graphite"]];
615     }
616     else
617     {
618         o_img_play_pressed = [NSImage imageNamed: @"play_blue"];
619         o_img_pause_pressed = [NSImage imageNamed: @"pause_blue"];
620         
621         [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_blue"]];
622         [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_blue"]];
623         [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_blue"]];
624         [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_blue"]];
625         [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_blue"]];
626         [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_blue"]];
627         [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_blue"]];
628         [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_blue"]];
629     }
630     
631     if( b_playing )
632         [o_btn_play setAlternateImage: o_img_play_pressed];
633     else
634         [o_btn_play setAlternateImage: o_img_pause_pressed];
635 }
636
637 - (void)initStrings
638 {
639     [o_window setTitle: _NS("VLC - Controller")];
640     [self setScrollField:_NS("VLC media player") stopAfter:-1];
641
642     /* button controls */
643     [o_btn_prev setToolTip: _NS("Previous")];
644     [o_btn_rewind setToolTip: _NS("Rewind")];
645     [o_btn_play setToolTip: _NS("Play")];
646     [o_btn_stop setToolTip: _NS("Stop")];
647     [o_btn_ff setToolTip: _NS("Fast Forward")];
648     [o_btn_next setToolTip: _NS("Next")];
649     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
650     [o_volumeslider setToolTip: _NS("Volume")];
651     [o_timeslider setToolTip: _NS("Position")];
652     [o_btn_playlist setToolTip: _NS("Playlist")];
653
654     /* messages panel */
655     [o_msgs_panel setTitle: _NS("Messages")];
656     [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog...")];
657
658     /* main menu */
659     [o_mi_about setTitle: [_NS("About VLC media player") \
660         stringByAppendingString: @"..."]];
661     [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
662     [o_mi_prefs setTitle: _NS("Preferences...")];
663     [o_mi_add_intf setTitle: _NS("Add Interface")];
664     [o_mu_add_intf setTitle: _NS("Add Interface")];
665     [o_mi_services setTitle: _NS("Services")];
666     [o_mi_hide setTitle: _NS("Hide VLC")];
667     [o_mi_hide_others setTitle: _NS("Hide Others")];
668     [o_mi_show_all setTitle: _NS("Show All")];
669     [o_mi_quit setTitle: _NS("Quit VLC")];
670
671     [o_mu_file setTitle: _ANS("1:File")];
672     [o_mi_open_generic setTitle: _NS("Open File...")];
673     [o_mi_open_file setTitle: _NS("Quick Open File...")];
674     [o_mi_open_disc setTitle: _NS("Open Disc...")];
675     [o_mi_open_net setTitle: _NS("Open Network...")];
676     [o_mi_open_recent setTitle: _NS("Open Recent")];
677     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
678     [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
679
680     [o_mu_edit setTitle: _NS("Edit")];
681     [o_mi_cut setTitle: _NS("Cut")];
682     [o_mi_copy setTitle: _NS("Copy")];
683     [o_mi_paste setTitle: _NS("Paste")];
684     [o_mi_clear setTitle: _NS("Clear")];
685     [o_mi_select_all setTitle: _NS("Select All")];
686
687     [o_mu_controls setTitle: _NS("Playback")];
688     [o_mi_play setTitle: _NS("Play")];
689     [o_mi_stop setTitle: _NS("Stop")];
690     [o_mi_faster setTitle: _NS("Faster")];
691     [o_mi_slower setTitle: _NS("Slower")];
692     [o_mi_previous setTitle: _NS("Previous")];
693     [o_mi_next setTitle: _NS("Next")];
694     [o_mi_random setTitle: _NS("Random")];
695     [o_mi_repeat setTitle: _NS("Repeat One")];
696     [o_mi_loop setTitle: _NS("Repeat All")];
697     [o_mi_fwd setTitle: _NS("Step Forward")];
698     [o_mi_bwd setTitle: _NS("Step Backward")];
699
700     [o_mi_program setTitle: _NS("Program")];
701     [o_mu_program setTitle: _NS("Program")];
702     [o_mi_title setTitle: _NS("Title")];
703     [o_mu_title setTitle: _NS("Title")];
704     [o_mi_chapter setTitle: _NS("Chapter")];
705     [o_mu_chapter setTitle: _NS("Chapter")];
706
707     [o_mu_audio setTitle: _NS("Audio")];
708     [o_mi_vol_up setTitle: _NS("Volume Up")];
709     [o_mi_vol_down setTitle: _NS("Volume Down")];
710     [o_mi_mute setTitle: _NS("Mute")];
711     [o_mi_audiotrack setTitle: _NS("Audio Track")];
712     [o_mu_audiotrack setTitle: _NS("Audio Track")];
713     [o_mi_channels setTitle: _NS("Audio Channels")];
714     [o_mu_channels setTitle: _NS("Audio Channels")];
715     [o_mi_device setTitle: _NS("Audio Device")];
716     [o_mu_device setTitle: _NS("Audio Device")];
717     [o_mi_visual setTitle: _NS("Visualizations")];
718     [o_mu_visual setTitle: _NS("Visualizations")];
719
720     [o_mu_video setTitle: _NS("Video")];
721     [o_mi_half_window setTitle: _NS("Half Size")];
722     [o_mi_normal_window setTitle: _NS("Normal Size")];
723     [o_mi_double_window setTitle: _NS("Double Size")];
724     [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
725     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
726     [o_mi_floatontop setTitle: _NS("Float on Top")];
727     [o_mi_snapshot setTitle: _NS("Snapshot")];
728     [o_mi_videotrack setTitle: _NS("Video Track")];
729     [o_mu_videotrack setTitle: _NS("Video Track")];
730     [o_mi_aspect_ratio setTitle: _NS("Aspect-ratio")];
731     [o_mu_aspect_ratio setTitle: _NS("Aspect-ratio")];
732     [o_mi_crop setTitle: _NS("Crop")];
733     [o_mu_crop setTitle: _NS("Crop")];
734     [o_mi_screen setTitle: _NS("Fullscreen Video Device")];
735     [o_mu_screen setTitle: _NS("Fullscreen Video Device")];
736     [o_mi_subtitle setTitle: _NS("Subtitles Track")];
737     [o_mu_subtitle setTitle: _NS("Subtitles Track")];
738     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
739     [o_mu_deinterlace setTitle: _NS("Deinterlace")];
740     [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
741     [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
742
743     [o_mu_window setTitle: _NS("Window")];
744     [o_mi_minimize setTitle: _NS("Minimize Window")];
745     [o_mi_close_window setTitle: _NS("Close Window")];
746     [o_mi_controller setTitle: _NS("Controller...")];
747     [o_mi_equalizer setTitle: _NS("Equalizer...")];
748     [o_mi_extended setTitle: _NS("Extended Controls...")];
749     [o_mi_bookmarks setTitle: _NS("Bookmarks...")];
750     [o_mi_playlist setTitle: _NS("Playlist...")];
751     [o_mi_info setTitle: _NS("Media Information...")];
752     [o_mi_messages setTitle: _NS("Messages...")];
753     [o_mi_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
754
755     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
756
757     [o_mu_help setTitle: _NS("Help")];
758     [o_mi_help setTitle: _NS("VLC media player Help...")];
759     [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
760     [o_mi_license setTitle: _NS("License")];
761     [o_mi_documentation setTitle: _NS("Online Documentation...")];
762     [o_mi_website setTitle: _NS("VideoLAN Website...")];
763     [o_mi_donation setTitle: _NS("Make a donation...")];
764     [o_mi_forum setTitle: _NS("Online Forum...")];
765
766     /* dock menu */
767     [o_dmi_play setTitle: _NS("Play")];
768     [o_dmi_stop setTitle: _NS("Stop")];
769     [o_dmi_next setTitle: _NS("Next")];
770     [o_dmi_previous setTitle: _NS("Previous")];
771     [o_dmi_mute setTitle: _NS("Mute")];
772  
773     /* vout menu */
774     [o_vmi_play setTitle: _NS("Play")];
775     [o_vmi_stop setTitle: _NS("Stop")];
776     [o_vmi_prev setTitle: _NS("Previous")];
777     [o_vmi_next setTitle: _NS("Next")];
778     [o_vmi_volup setTitle: _NS("Volume Up")];
779     [o_vmi_voldown setTitle: _NS("Volume Down")];
780     [o_vmi_mute setTitle: _NS("Mute")];
781     [o_vmi_fullscreen setTitle: _NS("Fullscreen")];
782     [o_vmi_snapshot setTitle: _NS("Snapshot")];
783 }
784
785 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
786 {
787     o_msg_lock = [[NSLock alloc] init];
788     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
789
790     [p_intf->p_sys->o_sendport setDelegate: self];
791     [[NSRunLoop currentRunLoop]
792         addPort: p_intf->p_sys->o_sendport
793         forMode: NSDefaultRunLoopMode];
794
795     /* FIXME: don't poll */
796     [NSTimer scheduledTimerWithTimeInterval: 0.5
797         target: self selector: @selector(manageIntf:)
798         userInfo: nil repeats: FALSE];
799
800     /* FIXME: don't poll */
801     manageThread = [[NSThread alloc] initWithTarget:self selector:@selector(manage)
802                                      object: nil];
803
804     [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
805         var: "intf-add" selector: @selector(toggleVar:)];
806
807     /* check whether the user runs a valid version of OSX; alert is auto-released */
808     if( MACOS_VERSION < 10.4f )
809     {
810         NSAlert *ourAlert;
811         int i_returnValue;
812         ourAlert = [NSAlert alertWithMessageText: _NS("Your version of Mac OS X is not supported")
813                         defaultButton: _NS("Quit")
814                       alternateButton: NULL
815                           otherButton: NULL
816             informativeTextWithFormat: _NS("VLC media player requires Mac OS X 10.4 or higher.")];
817         [ourAlert setAlertStyle: NSCriticalAlertStyle];
818         i_returnValue = [ourAlert runModal];
819         [NSApp terminate: self];
820     }
821
822     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
823 }
824
825 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
826 {
827     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
828     NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
829     if( b_autoplay )
830         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
831     else
832         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: YES];
833
834     return( TRUE );
835 }
836
837 - (NSString *)localizedString:(const char *)psz
838 {
839     NSString * o_str = nil;
840
841     if( psz != NULL )
842     {
843         o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
844
845         if( o_str == NULL )
846         {
847             msg_Err( VLCIntf, "could not translate: %s", psz );
848             return( @"" );
849         }
850     }
851     else
852     {
853         msg_Warn( VLCIntf, "can't translate empty strings" );
854         return( @"" );
855     }
856
857     return( o_str );
858 }
859
860 /* When user click in the Dock icon our double click in the finder */
861 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
862 {    
863     if(!hasVisibleWindows)
864         [o_window makeKeyAndOrderFront:self];
865
866     return YES;
867 }
868
869 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
870 {
871 #ifdef UPDATE_CHECK
872     /* Check for update silently on startup */
873     if( !nib_update_loaded )
874         nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self];
875
876     if([o_update shouldCheckForUpdate])
877         [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:NULL];
878 #endif
879
880     /* Handle sleep notification */
881     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
882            name:NSWorkspaceWillSleepNotification object:nil];
883 }
884
885 /* Listen to the remote in exclusive mode, only when VLC is the active
886    application */
887 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
888 {
889     [o_remote startListening: self];
890 }
891 - (void)applicationDidResignActive:(NSNotification *)aNotification
892 {
893     [o_remote stopListening: self];
894 }
895
896 /* Triggered when the computer goes to sleep */
897 - (void)computerWillSleep: (NSNotification *)notification
898 {
899     /* Pause */
900     if( p_intf->p_sys->i_play_status == PLAYING_S )
901     {
902         vlc_value_t val;
903         val.i_int = config_GetInt( p_intf, "key-play-pause" );
904         var_Set( p_intf->p_libvlc, "key-pressed", val );
905     }
906 }
907
908 /* Helper method for the remote control interface in order to trigger forward/backward and volume
909    increase/decrease as long as the user holds the left/right, plus/minus button */
910 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
911 {
912     if(b_remote_button_hold)
913     {
914         switch([buttonIdentifierNumber intValue])
915         {
916             case kRemoteButtonRight_Hold:
917                   [o_controls forward: self];
918             break;
919             case kRemoteButtonLeft_Hold:
920                   [o_controls backward: self];
921             break;
922             case kRemoteButtonVolume_Plus_Hold:
923                 [o_controls volumeUp: self];
924             break;
925             case kRemoteButtonVolume_Minus_Hold:
926                 [o_controls volumeDown: self];
927             break;
928         }
929         if(b_remote_button_hold)
930         {
931             /* trigger event */
932             [self performSelector:@selector(executeHoldActionForRemoteButton:)
933                          withObject:buttonIdentifierNumber
934                          afterDelay:0.25];
935         }
936     }
937 }
938
939 /* Apple Remote callback */
940 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
941                pressedDown: (BOOL) pressedDown
942                 clickCount: (unsigned int) count
943 {
944     switch( buttonIdentifier )
945     {
946         case kRemoteButtonPlay:
947             if(count >= 2) {
948                 [o_controls toogleFullscreen:self];
949             } else {
950                 [o_controls play: self];
951             }
952             break;
953         case kRemoteButtonVolume_Plus:
954             [o_controls volumeUp: self];
955             break;
956         case kRemoteButtonVolume_Minus:
957             [o_controls volumeDown: self];
958             break;
959         case kRemoteButtonRight:
960             [o_controls next: self];
961             break;
962         case kRemoteButtonLeft:
963             [o_controls prev: self];
964             break;
965         case kRemoteButtonRight_Hold:
966         case kRemoteButtonLeft_Hold:
967         case kRemoteButtonVolume_Plus_Hold:
968         case kRemoteButtonVolume_Minus_Hold:
969             /* simulate an event as long as the user holds the button */
970             b_remote_button_hold = pressedDown;
971             if( pressedDown )
972             {
973                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];
974                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
975                            withObject:buttonIdentifierNumber];
976             }
977             break;
978         case kRemoteButtonMenu:
979             [o_controls showPosition: self];
980             break;
981         default:
982             /* Add here whatever you want other buttons to do */
983             break;
984     }
985 }
986
987 - (char *)delocalizeString:(NSString *)id
988 {
989     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
990                           allowLossyConversion: NO];
991     char * psz_string;
992
993     if( o_data == nil )
994     {
995         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
996                      allowLossyConversion: YES];
997         psz_string = malloc( [o_data length] + 1 );
998         [o_data getBytes: psz_string];
999         psz_string[ [o_data length] ] = '\0';
1000         msg_Err( VLCIntf, "cannot convert to the requested encoding: %s",
1001                  psz_string );
1002     }
1003     else
1004     {
1005         psz_string = malloc( [o_data length] + 1 );
1006         [o_data getBytes: psz_string];
1007         psz_string[ [o_data length] ] = '\0';
1008     }
1009
1010     return psz_string;
1011 }
1012
1013 /* i_width is in pixels */
1014 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
1015 {
1016     NSMutableString *o_wrapped;
1017     NSString *o_out_string;
1018     NSRange glyphRange, effectiveRange, charRange;
1019     NSRect lineFragmentRect;
1020     unsigned glyphIndex, breaksInserted = 0;
1021
1022     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
1023         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
1024         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
1025     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
1026     NSTextContainer *o_container = [[NSTextContainer alloc]
1027         initWithContainerSize: NSMakeSize(i_width, 2000)];
1028
1029     [o_layout_manager addTextContainer: o_container];
1030     [o_container release];
1031     [o_storage addLayoutManager: o_layout_manager];
1032     [o_layout_manager release];
1033
1034     o_wrapped = [o_in_string mutableCopy];
1035     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
1036
1037     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
1038             glyphIndex += effectiveRange.length) {
1039         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
1040                                             effectiveRange: &effectiveRange];
1041         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
1042                                     actualGlyphRange: &effectiveRange];
1043         if([o_wrapped lineRangeForRange:
1044                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
1045             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
1046             breaksInserted++;
1047         }
1048     }
1049     o_out_string = [NSString stringWithString: o_wrapped];
1050     [o_wrapped release];
1051     [o_storage release];
1052
1053     return o_out_string;
1054 }
1055
1056
1057 /*****************************************************************************
1058  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1059  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1060  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1061  *****************************************************************************/
1062 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
1063 {
1064     unichar key = 0;
1065     vlc_value_t val;
1066     unsigned int i_pressed_modifiers = 0;
1067     struct hotkey *p_hotkeys;
1068     int i;
1069
1070     val.i_int = 0;
1071     p_hotkeys = p_intf->p_libvlc->p_hotkeys;
1072
1073     i_pressed_modifiers = [o_event modifierFlags];
1074
1075     if( i_pressed_modifiers & NSShiftKeyMask )
1076         val.i_int |= KEY_MODIFIER_SHIFT;
1077     if( i_pressed_modifiers & NSControlKeyMask )
1078         val.i_int |= KEY_MODIFIER_CTRL;
1079     if( i_pressed_modifiers & NSAlternateKeyMask )
1080         val.i_int |= KEY_MODIFIER_ALT;
1081     if( i_pressed_modifiers & NSCommandKeyMask )
1082         val.i_int |= KEY_MODIFIER_COMMAND;
1083
1084     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1085
1086     switch( key )
1087     {
1088         case NSDeleteCharacter:
1089         case NSDeleteFunctionKey:
1090         case NSDeleteCharFunctionKey:
1091         case NSBackspaceCharacter:
1092         case NSUpArrowFunctionKey:
1093         case NSDownArrowFunctionKey:
1094         case NSRightArrowFunctionKey:
1095         case NSLeftArrowFunctionKey:
1096         case NSEnterCharacter:
1097         case NSCarriageReturnCharacter:
1098             return NO;
1099     }
1100
1101     val.i_int |= CocoaKeyToVLC( key );
1102
1103     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
1104     {
1105         if( p_hotkeys[i].i_key == val.i_int )
1106         {
1107             var_Set( p_intf->p_libvlc, "key-pressed", val );
1108             return YES;
1109         }
1110     }
1111
1112     return NO;
1113 }
1114
1115 - (id)getControls
1116 {
1117     if( o_controls )
1118         return o_controls;
1119
1120     return nil;
1121 }
1122
1123 - (id)getSimplePreferences
1124 {
1125     if( !o_sprefs )
1126         return nil;
1127
1128     if( !nib_prefs_loaded )
1129         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: self];
1130
1131     return o_sprefs;
1132 }
1133
1134 - (id)getPreferences
1135 {
1136     if( !o_prefs )
1137         return nil;
1138
1139     if( !nib_prefs_loaded )
1140         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: self];
1141
1142     return o_prefs;
1143 }
1144
1145 - (id)getPlaylist
1146 {
1147     if( o_playlist )
1148         return o_playlist;
1149
1150     return nil;
1151 }
1152
1153 - (id)getInfo
1154 {
1155     if( o_info )
1156         return o_info;
1157
1158     return nil;
1159 }
1160
1161 - (id)getWizard
1162 {
1163     if( o_wizard )
1164         return o_wizard;
1165
1166     return nil;
1167 }
1168
1169 - (id)getBookmarks
1170 {
1171     if( o_bookmarks )
1172         return o_bookmarks;
1173
1174     return nil;
1175 }
1176
1177 - (id)getEmbeddedList
1178 {
1179     if( o_embedded_list )
1180         return o_embedded_list;
1181
1182     return nil;
1183 }
1184
1185 - (id)getInteractionList
1186 {
1187     if( o_interaction_list )
1188         return o_interaction_list;
1189
1190     return nil;
1191 }
1192
1193 - (id)getMainIntfPgbar
1194 {
1195     if( o_main_pgbar )
1196         return o_main_pgbar;
1197
1198     return nil;
1199 }
1200
1201 - (id)getControllerWindow
1202 {
1203     if( o_window )
1204         return o_window;
1205     return nil;
1206 }
1207
1208 - (id)getVoutMenu
1209 {
1210     return o_vout_menu;
1211 }
1212
1213 - (id)getEyeTVController
1214 {
1215     if( o_eyetv )
1216         return o_eyetv;
1217
1218     return nil;
1219 }
1220
1221 - (void)manage
1222 {
1223     playlist_t * p_playlist;
1224
1225     /* new thread requires a new pool */
1226     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1227
1228     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
1229
1230     p_playlist = pl_Yield( p_intf );
1231
1232     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
1233     var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
1234     var_AddCallback( p_playlist, "item-append", PlaylistChanged, self );
1235     var_AddCallback( p_playlist, "item-deleted", PlaylistChanged, self );
1236     var_AddCallback( p_playlist, "playlist-current", PlaylistChanged, self );
1237
1238     vlc_object_release( p_playlist );
1239
1240     vlc_object_lock( p_intf );
1241     while( vlc_object_alive( p_intf ) )
1242     {
1243         vlc_mutex_lock( &p_intf->change_lock );
1244
1245         if( p_intf->p_sys->p_input == NULL )
1246         {
1247             p_intf->p_sys->p_input = p_playlist->p_input;
1248
1249                 /* Refresh the interface */
1250             if( p_intf->p_sys->p_input )
1251             {
1252                 msg_Dbg( p_intf, "input has changed, refreshing interface" );
1253                 p_intf->p_sys->b_input_update = true;
1254             }
1255         }
1256         else if( p_intf->p_sys->p_input->b_die || p_intf->p_sys->p_input->b_dead )
1257         {
1258             /* input stopped */
1259             p_intf->p_sys->b_intf_update = true;
1260             p_intf->p_sys->i_play_status = END_S;
1261             msg_Dbg( p_intf, "input has stopped, refreshing interface" );
1262             p_intf->p_sys->p_input = NULL;
1263         }
1264
1265         /* Manage volume status */
1266         [self manageVolumeSlider];
1267
1268         vlc_mutex_unlock( &p_intf->change_lock );
1269         vlc_object_unlock( p_intf );
1270         msleep( 100000 );
1271         vlc_object_lock( p_intf );
1272     }
1273     vlc_object_unlock( p_intf );
1274     [o_pool release];
1275 }
1276
1277 - (void)manageIntf:(NSTimer *)o_timer
1278 {
1279     vlc_value_t val;
1280     playlist_t * p_playlist;
1281     input_thread_t * p_input;
1282
1283     vlc_object_lock( p_intf );
1284
1285     if( !vlc_object_alive( p_intf ) )
1286     {
1287         vlc_object_unlock( p_intf );
1288         [o_timer invalidate];
1289         return;
1290     }
1291
1292     if( p_intf->p_sys->b_input_update )
1293     {
1294         /* Called when new input is opened */
1295         p_intf->p_sys->b_current_title_update = true;
1296         p_intf->p_sys->b_intf_update = true;
1297         p_intf->p_sys->b_input_update = false;
1298     }
1299     if( p_intf->p_sys->b_intf_update )
1300     {
1301         bool b_input = false;
1302         bool b_plmul = false;
1303         bool b_control = false;
1304         bool b_seekable = false;
1305         bool b_chapters = false;
1306
1307         playlist_t * p_playlist = pl_Yield( p_intf );
1308     /* TODO: fix i_size use */
1309         b_plmul = p_playlist->items.i_size > 1;
1310
1311         p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
1312                                    FIND_CHILD );
1313
1314         if( ( b_input = ( p_input != NULL ) ) )
1315         {
1316             /* seekable streams */
1317             b_seekable = var_GetBool( p_input, "seekable" );
1318
1319             /* check whether slow/fast motion is possible */
1320             b_control = p_input->b_can_pace_control;
1321
1322             /* chapters & titles */
1323             //b_chapters = p_input->stream.i_area_nb > 1;
1324             vlc_object_release( p_input );
1325         }
1326         vlc_object_release( p_playlist );
1327
1328         [o_btn_stop setEnabled: b_input];
1329         [o_btn_ff setEnabled: b_seekable];
1330         [o_btn_rewind setEnabled: b_seekable];
1331         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
1332         [o_btn_next setEnabled: (b_plmul || b_chapters)];
1333
1334         [o_timeslider setFloatValue: 0.0];
1335         [o_timeslider setEnabled: b_seekable];
1336         [o_timefield setStringValue: @"00:00"];
1337         [[[self getControls] getFSPanel] setStreamPos: 0 andTime: @"00:00"];
1338         [[[self getControls] getFSPanel] setSeekable: b_seekable];
1339
1340         [o_embedded_window setSeekable: b_seekable];
1341
1342         p_intf->p_sys->b_current_title_update = true;
1343         
1344         p_intf->p_sys->b_intf_update = false;
1345     }
1346
1347     if( p_intf->p_sys->b_playmode_update )
1348     {
1349         [o_playlist playModeUpdated];
1350         p_intf->p_sys->b_playmode_update = false;
1351     }
1352     if( p_intf->p_sys->b_playlist_update )
1353     {
1354         [o_playlist playlistUpdated];
1355         p_intf->p_sys->b_playlist_update = false;
1356     }
1357
1358     if( p_intf->p_sys->b_fullscreen_update )
1359     {
1360         p_intf->p_sys->b_fullscreen_update = false;
1361     }
1362
1363     if( p_intf->p_sys->b_intf_show )
1364     {
1365         [o_window makeKeyAndOrderFront: self];
1366
1367         p_intf->p_sys->b_intf_show = false;
1368     }
1369
1370     p_playlist = pl_Yield( p_intf );
1371     p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
1372                                FIND_CHILD );
1373
1374     if( p_input && !p_input->b_die )
1375     {
1376         vlc_value_t val;
1377
1378         if( p_intf->p_sys->b_current_title_update )
1379         {
1380             NSString *o_temp;
1381
1382             if( p_playlist->status.p_item == NULL )
1383             {
1384                 vlc_object_release( p_input );
1385                 vlc_object_release( p_playlist );
1386                 return;
1387             }
1388             if( input_item_GetNowPlaying ( p_playlist->status.p_item->p_input ) )
1389                 o_temp = [NSString stringWithUTF8String: 
1390                     input_item_GetNowPlaying ( p_playlist->status.p_item->p_input )];
1391             else
1392                 o_temp = [NSString stringWithUTF8String:
1393                     p_playlist->status.p_item->p_input->psz_name];
1394             [self setScrollField: o_temp stopAfter:-1];
1395             [[[self getControls] getFSPanel] setStreamTitle: o_temp];
1396
1397             [[o_controls getVoutView] updateTitle];
1398  
1399             [o_playlist updateRowSelection];
1400             p_intf->p_sys->b_current_title_update = FALSE;
1401         }
1402
1403         if( [o_timeslider isEnabled] )
1404         {
1405             /* Update the slider */
1406             vlc_value_t time;
1407             NSString * o_time;
1408             vlc_value_t pos;
1409             char psz_time[MSTRTIME_MAX_SIZE];
1410             float f_updated;
1411
1412             var_Get( p_input, "position", &pos );
1413             f_updated = 10000. * pos.f_float;
1414             [o_timeslider setFloatValue: f_updated];
1415
1416             var_Get( p_input, "time", &time );
1417
1418             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1419
1420             [o_timefield setStringValue: o_time];
1421             [[[self getControls] getFSPanel] setStreamPos: f_updated andTime: o_time];
1422             [o_embedded_window setTime: o_time position: f_updated];
1423         }
1424
1425         if( p_intf->p_sys->b_volume_update )
1426         {
1427             NSString *o_text;
1428             int i_volume_step = 0;
1429             o_text = [NSString stringWithFormat: _NS("Volume: %d%%"), i_lastShownVolume * 400 / AOUT_VOLUME_MAX];
1430             if( i_lastShownVolume != -1 )
1431             [self setScrollField:o_text stopAfter:1000000];
1432             i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
1433             [o_volumeslider setFloatValue: (float)i_lastShownVolume / i_volume_step];
1434             [o_volumeslider setEnabled: TRUE];
1435             [[[self getControls] getFSPanel] setVolumeLevel: (float)i_lastShownVolume / i_volume_step];
1436             p_intf->p_sys->b_mute = ( i_lastShownVolume == 0 );
1437             p_intf->p_sys->b_volume_update = FALSE;
1438         }
1439
1440         /* Manage Playing status */
1441         var_Get( p_input, "state", &val );
1442         if( p_intf->p_sys->i_play_status != val.i_int )
1443         {
1444             p_intf->p_sys->i_play_status = val.i_int;
1445             [self playStatusUpdated: p_intf->p_sys->i_play_status];
1446             [o_embedded_window playStatusUpdated: p_intf->p_sys->i_play_status];
1447         }
1448         vlc_object_release( p_input );
1449     }
1450     else
1451     {
1452         p_intf->p_sys->i_play_status = END_S;
1453         p_intf->p_sys->b_intf_update = true;
1454         [self playStatusUpdated: p_intf->p_sys->i_play_status];
1455         [o_embedded_window playStatusUpdated: p_intf->p_sys->i_play_status];
1456         [self setSubmenusEnabled: FALSE];
1457     }
1458     vlc_object_release( p_playlist );
1459
1460     [self updateMessageArray];
1461
1462     if( ((i_end_scroll != -1) && (mdate() > i_end_scroll)) || !p_input )
1463         [self resetScrollField];
1464
1465     [NSTimer scheduledTimerWithTimeInterval: 0.3
1466         target: self selector: @selector(manageIntf:)
1467         userInfo: nil repeats: FALSE];
1468     vlc_object_unlock( p_intf );
1469 }
1470
1471 - (void)setupMenus
1472 {
1473     playlist_t * p_playlist = pl_Yield( p_intf );
1474     input_thread_t * p_input = p_playlist->p_input;
1475     if( p_input != NULL )
1476     {
1477         vlc_object_yield( p_input );
1478         [o_controls setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
1479             var: "program" selector: @selector(toggleVar:)];
1480
1481         [o_controls setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
1482             var: "title" selector: @selector(toggleVar:)];
1483
1484         [o_controls setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
1485             var: "chapter" selector: @selector(toggleVar:)];
1486
1487         [o_controls setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
1488             var: "audio-es" selector: @selector(toggleVar:)];
1489
1490         [o_controls setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
1491             var: "video-es" selector: @selector(toggleVar:)];
1492
1493         [o_controls setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
1494             var: "spu-es" selector: @selector(toggleVar:)];
1495
1496         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
1497                                                     FIND_ANYWHERE );
1498         if( p_aout != NULL )
1499         {
1500             [o_controls setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
1501                 var: "audio-channels" selector: @selector(toggleVar:)];
1502
1503             [o_controls setupVarMenuItem: o_mi_device target: (vlc_object_t *)p_aout
1504                 var: "audio-device" selector: @selector(toggleVar:)];
1505
1506             [o_controls setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
1507                 var: "visual" selector: @selector(toggleVar:)];
1508             vlc_object_release( (vlc_object_t *)p_aout );
1509         }
1510
1511         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1512                                                             FIND_ANYWHERE );
1513
1514         if( p_vout != NULL )
1515         {
1516             vlc_object_t * p_dec_obj;
1517
1518             [o_controls setupVarMenuItem: o_mi_aspect_ratio target: (vlc_object_t *)p_vout
1519                 var: "aspect-ratio" selector: @selector(toggleVar:)];
1520
1521             [o_controls setupVarMenuItem: o_mi_crop target: (vlc_object_t *) p_vout
1522                 var: "crop" selector: @selector(toggleVar:)];
1523
1524             [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
1525                 var: "video-device" selector: @selector(toggleVar:)];
1526
1527             [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
1528                 var: "deinterlace" selector: @selector(toggleVar:)];
1529
1530             p_dec_obj = (vlc_object_t *)vlc_object_find(
1531                                                  (vlc_object_t *)p_vout,
1532                                                  VLC_OBJECT_DECODER,
1533                                                  FIND_PARENT );
1534             if( p_dec_obj != NULL )
1535             {
1536                [o_controls setupVarMenuItem: o_mi_ffmpeg_pp target:
1537                     (vlc_object_t *)p_dec_obj var:"ffmpeg-pp-q" selector:
1538                     @selector(toggleVar:)];
1539
1540                 vlc_object_release(p_dec_obj);
1541             }
1542             vlc_object_release( (vlc_object_t *)p_vout );
1543         }
1544         vlc_object_release( p_input );
1545     }
1546     vlc_object_release( p_playlist );
1547 }
1548
1549 - (void)refreshVoutDeviceMenu:(NSNotification *)o_notification
1550 {
1551     int x,y = 0;
1552     vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1553                                               FIND_ANYWHERE );
1554  
1555     if(! p_vout )
1556         return;
1557  
1558     /* clean the menu before adding new entries */
1559     if( [o_mi_screen hasSubmenu] )
1560     {
1561         y = [[o_mi_screen submenu] numberOfItems] - 1;
1562         msg_Dbg( VLCIntf, "%i items in submenu", y );
1563         while( x != y )
1564         {
1565             msg_Dbg( VLCIntf, "removing item %i of %i", x, y );
1566             [[o_mi_screen submenu] removeItemAtIndex: x];
1567             x++;
1568         }
1569     }
1570
1571     [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
1572                              var: "video-device" selector: @selector(toggleVar:)];
1573     vlc_object_release( (vlc_object_t *)p_vout );
1574 }
1575
1576 - (void)setScrollField:(NSString *)o_string stopAfter:(int)timeout
1577 {
1578     if( timeout != -1 )
1579         i_end_scroll = mdate() + timeout;
1580     else
1581         i_end_scroll = -1;
1582     [o_scrollfield setStringValue: o_string];
1583 }
1584
1585 - (void)resetScrollField
1586 {
1587     playlist_t * p_playlist = pl_Yield( p_intf );
1588     input_thread_t * p_input = p_playlist->p_input;
1589
1590     i_end_scroll = -1;
1591     if( p_input && !p_input->b_die )
1592     {
1593         NSString *o_temp;
1594         vlc_object_yield( p_input );
1595         if( input_item_GetNowPlaying ( p_playlist->status.p_item->p_input ) )
1596             o_temp = [NSString stringWithUTF8String: 
1597                 input_item_GetNowPlaying ( p_playlist->status.p_item->p_input )];
1598         else
1599             o_temp = [NSString stringWithUTF8String:
1600                 p_playlist->status.p_item->p_input->psz_name];
1601         [self setScrollField: o_temp stopAfter:-1];
1602         vlc_object_release( p_input );
1603         vlc_object_release( p_playlist );
1604         return;
1605     }
1606     vlc_object_release( p_playlist );
1607     [self setScrollField: _NS("VLC media player") stopAfter:-1];
1608 }
1609
1610 - (void)updateMessageArray
1611 {
1612     int i_start, i_stop;
1613
1614     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1615     i_stop = *p_intf->p_sys->p_sub->pi_stop;
1616     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1617
1618     if( p_intf->p_sys->p_sub->i_start != i_stop )
1619     {
1620         NSColor *o_white = [NSColor whiteColor];
1621         NSColor *o_red = [NSColor redColor];
1622         NSColor *o_yellow = [NSColor yellowColor];
1623         NSColor *o_gray = [NSColor grayColor];
1624
1625         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1626         static const char * ppsz_type[4] = { ": ", " error: ",
1627                                              " warning: ", " debug: " };
1628
1629         for( i_start = p_intf->p_sys->p_sub->i_start;
1630              i_start != i_stop;
1631              i_start = (i_start+1) % VLC_MSG_QSIZE )
1632         {
1633             NSString *o_msg;
1634             NSDictionary *o_attr;
1635             NSAttributedString *o_msg_color;
1636
1637             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
1638
1639             [o_msg_lock lock];
1640
1641             if( [o_msg_arr count] + 2 > 400 )
1642             {
1643                 unsigned rid[] = { 0, 1 };
1644                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
1645                            numIndices: sizeof(rid)/sizeof(rid[0])];
1646             }
1647
1648             o_attr = [NSDictionary dictionaryWithObject: o_gray
1649                 forKey: NSForegroundColorAttributeName];
1650             o_msg = [NSString stringWithFormat: @"%s%s",
1651                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1652                 ppsz_type[i_type]];
1653             o_msg_color = [[NSAttributedString alloc]
1654                 initWithString: o_msg attributes: o_attr];
1655             [o_msg_arr addObject: [o_msg_color autorelease]];
1656
1657             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
1658                 forKey: NSForegroundColorAttributeName];
1659             o_msg = [NSString stringWithFormat: @"%s\n",
1660                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1661             o_msg_color = [[NSAttributedString alloc]
1662                 initWithString: o_msg attributes: o_attr];
1663             [o_msg_arr addObject: [o_msg_color autorelease]];
1664
1665             [o_msg_lock unlock];
1666         }
1667
1668         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1669         p_intf->p_sys->p_sub->i_start = i_start;
1670         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1671     }
1672 }
1673
1674 - (void)playStatusUpdated:(int)i_status
1675 {
1676     if( i_status == PLAYING_S )
1677     {
1678         [[[self getControls] getFSPanel] setPause];
1679         [o_btn_play setImage: o_img_pause];
1680         [o_btn_play setAlternateImage: o_img_pause_pressed];
1681         [o_btn_play setToolTip: _NS("Pause")];
1682         [o_mi_play setTitle: _NS("Pause")];
1683         [o_dmi_play setTitle: _NS("Pause")];
1684         [o_vmi_play setTitle: _NS("Pause")];
1685     }
1686     else
1687     {
1688         [[[self getControls] getFSPanel] setPlay];
1689         [o_btn_play setImage: o_img_play];
1690         [o_btn_play setAlternateImage: o_img_play_pressed];
1691         [o_btn_play setToolTip: _NS("Play")];
1692         [o_mi_play setTitle: _NS("Play")];
1693         [o_dmi_play setTitle: _NS("Play")];
1694         [o_vmi_play setTitle: _NS("Play")];
1695     }
1696 }
1697
1698 - (void)setSubmenusEnabled:(BOOL)b_enabled
1699 {
1700     [o_mi_program setEnabled: b_enabled];
1701     [o_mi_title setEnabled: b_enabled];
1702     [o_mi_chapter setEnabled: b_enabled];
1703     [o_mi_audiotrack setEnabled: b_enabled];
1704     [o_mi_visual setEnabled: b_enabled];
1705     [o_mi_videotrack setEnabled: b_enabled];
1706     [o_mi_subtitle setEnabled: b_enabled];
1707     [o_mi_channels setEnabled: b_enabled];
1708     [o_mi_deinterlace setEnabled: b_enabled];
1709     [o_mi_ffmpeg_pp setEnabled: b_enabled];
1710     [o_mi_device setEnabled: b_enabled];
1711     [o_mi_screen setEnabled: b_enabled];
1712     [o_mi_aspect_ratio setEnabled: b_enabled];
1713     [o_mi_crop setEnabled: b_enabled];
1714 }
1715
1716 - (void)manageVolumeSlider
1717 {
1718     audio_volume_t i_volume;
1719     aout_VolumeGet( p_intf, &i_volume );
1720
1721     if( i_volume != i_lastShownVolume )
1722     {
1723         i_lastShownVolume = i_volume;
1724         p_intf->p_sys->b_volume_update = TRUE;
1725     }
1726 }
1727
1728 - (IBAction)timesliderUpdate:(id)sender
1729 {
1730     float f_updated;
1731     playlist_t * p_playlist;
1732     input_thread_t * p_input;
1733
1734     switch( [[NSApp currentEvent] type] )
1735     {
1736         case NSLeftMouseUp:
1737         case NSLeftMouseDown:
1738         case NSLeftMouseDragged:
1739             f_updated = [sender floatValue];
1740             break;
1741
1742         default:
1743             return;
1744     }
1745     p_playlist = pl_Yield( p_intf );
1746     p_input = p_playlist->p_input;
1747     if( p_input != NULL )
1748     {
1749         vlc_value_t time;
1750         vlc_value_t pos;
1751         NSString * o_time;
1752         char psz_time[MSTRTIME_MAX_SIZE];
1753         vlc_object_yield( p_input );
1754
1755         pos.f_float = f_updated / 10000.;
1756         var_Set( p_input, "position", pos );
1757         [o_timeslider setFloatValue: f_updated];
1758
1759         var_Get( p_input, "time", &time );
1760
1761         o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1762         [o_timefield setStringValue: o_time];
1763         [[[self getControls] getFSPanel] setStreamPos: f_updated andTime: o_time];
1764         [o_embedded_window setTime: o_time position: f_updated];
1765         vlc_object_release( p_input );
1766     }
1767     vlc_object_release( p_playlist );
1768 }
1769
1770 - (void)applicationWillTerminate:(NSNotification *)notification
1771 {
1772     playlist_t * p_playlist;
1773     vout_thread_t * p_vout;
1774     int returnedValue = 0;
1775  
1776     msg_Dbg( p_intf, "Terminating" );
1777
1778     [manageThread cancel];
1779     [manageThread release];
1780
1781     /* make sure that the current volume is saved */
1782     config_PutInt( p_intf->p_libvlc, "volume", i_lastShownVolume );
1783     returnedValue = config_SaveConfigFile( p_intf->p_libvlc, "main" );
1784     if( returnedValue != 0 )
1785         msg_Err( p_intf,
1786                  "error while saving volume in osx's terminate method (%i)",
1787                  returnedValue );
1788
1789     /* save the prefs if they were changed in the extended panel */
1790     if(o_extended && [o_extended getConfigChanged])
1791     {
1792         [o_extended savePrefs];
1793     }
1794  
1795     p_intf->b_interaction = false;
1796     var_DelCallback( p_intf, "interaction", InteractCallback, self );
1797
1798     /* remove global observer watching for vout device changes correctly */
1799     [[NSNotificationCenter defaultCenter] removeObserver: self];
1800
1801     /* release some other objects here, because it isn't sure whether dealloc
1802      * will be called later on */
1803     
1804     if( nib_about_loaded )
1805         [o_about release];
1806     
1807     if( nib_prefs_loaded )
1808         [o_prefs release];
1809     
1810     if( nib_open_loaded )
1811         [o_open release];
1812  
1813     if( nib_extended_loaded )
1814     {
1815         [o_extended collapsAll];
1816         [o_extended release];
1817     }
1818  
1819     if( nib_bookmarks_loaded )
1820         [o_bookmarks release];
1821
1822     if( nib_info_loaded )
1823         [o_info release];
1824     
1825     if( nib_wizard_loaded )
1826         [o_wizard release];
1827  
1828     [o_embedded_list release];
1829     [o_interaction_list release];
1830     [o_eyetv release];
1831
1832     [o_img_pause_pressed release];
1833     [o_img_play_pressed release];
1834     [o_img_pause release];
1835     [o_img_play release];
1836
1837     [o_msg_arr removeAllObjects];
1838     [o_msg_arr release];
1839
1840     [o_msg_lock release];
1841
1842     /* write cached user defaults to disk */
1843     [[NSUserDefaults standardUserDefaults] synchronize];
1844
1845     vlc_object_kill( p_intf->p_libvlc );
1846
1847     /* Go back to Run() and make libvlc exit properly */
1848     if( jmpbuffer )
1849         longjmp( jmpbuffer, 1 );
1850     /* not reached */
1851 }
1852
1853
1854 - (IBAction)clearRecentItems:(id)sender
1855 {
1856     [[NSDocumentController sharedDocumentController]
1857                           clearRecentDocuments: nil];
1858 }
1859
1860 - (void)openRecentItem:(id)sender
1861 {
1862     [self application: nil openFile: [sender title]];
1863 }
1864
1865 - (IBAction)intfOpenFile:(id)sender
1866 {
1867     if( !nib_open_loaded )
1868     {
1869         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner:self];
1870         [o_open awakeFromNib];
1871         [o_open openFile];
1872     } else {
1873         [o_open openFile];
1874     }
1875 }
1876
1877 - (IBAction)intfOpenFileGeneric:(id)sender
1878 {
1879     if( !nib_open_loaded )
1880     {
1881         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner:self];
1882         [o_open awakeFromNib];
1883         [o_open openFileGeneric];
1884     } else {
1885         [o_open openFileGeneric];
1886     }
1887 }
1888
1889 - (IBAction)intfOpenDisc:(id)sender
1890 {
1891     if( !nib_open_loaded )
1892     {
1893         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner:self];
1894         [o_open awakeFromNib];
1895         [o_open openDisc];
1896     } else {
1897         [o_open openDisc];
1898     }
1899 }
1900
1901 - (IBAction)intfOpenNet:(id)sender
1902 {
1903     if( !nib_open_loaded )
1904     {
1905         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner:self];
1906         [o_open awakeFromNib];
1907         [o_open openNet];
1908     } else {
1909         [o_open openNet];
1910     }
1911 }
1912
1913 - (IBAction)showWizard:(id)sender
1914 {
1915     if( !nib_wizard_loaded )
1916     {
1917         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner:self];
1918         [o_wizard initStrings];
1919         [o_wizard resetWizard];
1920         [o_wizard showWizard];
1921     } else {
1922         [o_wizard resetWizard];
1923         [o_wizard showWizard];
1924     }
1925 }
1926
1927 - (IBAction)showExtended:(id)sender
1928 {
1929     if( o_extended == nil )
1930     {
1931         o_extended = [[VLCExtended alloc] init];
1932     }
1933     if( !nib_extended_loaded )
1934     {
1935         nib_extended_loaded = [NSBundle loadNibNamed:@"Extended" owner:self];
1936         [o_extended initStrings];
1937         [o_extended showPanel];
1938     } else {
1939         [o_extended showPanel];
1940     }
1941 }
1942
1943 - (IBAction)showSFilters:(id)sender
1944 {
1945     if( o_sfilters == nil )
1946     {
1947         o_sfilters = [[VLCsFilters alloc] init];
1948     }
1949     if( !nib_sfilters_loaded )
1950     {
1951         nib_sfilters_loaded = [NSBundle loadNibNamed:@"SFilters" owner:self];
1952         [o_sfilters initStrings];
1953         [o_sfilters showAsPanel];
1954     } else {
1955         [o_sfilters showAsPanel];
1956     }
1957 }
1958
1959 - (IBAction)showBookmarks:(id)sender
1960 {
1961     /* we need the wizard-nib for the bookmarks's extract functionality */
1962     if( !nib_wizard_loaded )
1963     {
1964         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner:self];
1965         [o_wizard initStrings];
1966     }
1967  
1968     if( !nib_bookmarks_loaded )
1969         nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner:self];
1970
1971     [o_bookmarks showBookmarks];
1972 }
1973
1974 - (IBAction)viewAbout:(id)sender
1975 {
1976     if( !nib_about_loaded )
1977         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner:self];
1978
1979     [o_about showAbout];
1980 }
1981
1982 - (IBAction)showLicense:(id)sender
1983 {
1984     if( !nib_about_loaded )
1985         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner:self];
1986
1987     [o_about showGPL: sender];
1988 }
1989     
1990 - (IBAction)viewPreferences:(id)sender
1991 {
1992     if( !nib_prefs_loaded )
1993         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: self];
1994
1995     if( sender == o_mi_sprefs )
1996     {
1997         o_sprefs = [[VLCSimplePrefs alloc] init];
1998         [o_sprefs showSimplePrefs];
1999     }
2000     else
2001         [o_prefs showPrefs];
2002 }
2003
2004 - (IBAction)checkForUpdate:(id)sender
2005 {
2006 #ifdef UPDATE_CHECK
2007     if( !nib_update_loaded )
2008         nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self];
2009     [o_update showUpdateWindow];
2010 #else
2011     msg_Err( VLCIntf, "Update checker wasn't enabled in this build" );
2012     intf_UserFatal( VLCIntf, false, _("Update check failed"), _("Checking for updates was not enabled in this build.") );
2013 #endif
2014 }
2015
2016 - (IBAction)viewHelp:(id)sender
2017 {
2018     if( !nib_about_loaded )
2019     {
2020         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner:self];
2021         [o_about showHelp];
2022     }
2023     else
2024         [o_about showHelp];
2025 }
2026
2027 - (IBAction)openReadMe:(id)sender
2028 {
2029     NSString * o_path = [[NSBundle mainBundle]
2030         pathForResource: @"README.MacOSX" ofType: @"rtf"];
2031
2032     [[NSWorkspace sharedWorkspace] openFile: o_path
2033                                    withApplication: @"TextEdit"];
2034 }
2035
2036 - (IBAction)openDocumentation:(id)sender
2037 {
2038     NSURL * o_url = [NSURL URLWithString:
2039         @"http://www.videolan.org/doc/"];
2040
2041     [[NSWorkspace sharedWorkspace] openURL: o_url];
2042 }
2043
2044 - (IBAction)openWebsite:(id)sender
2045 {
2046     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
2047
2048     [[NSWorkspace sharedWorkspace] openURL: o_url];
2049 }
2050
2051 - (IBAction)openForum:(id)sender
2052 {
2053     NSURL * o_url = [NSURL URLWithString: @"http://forum.videolan.org/"];
2054
2055     [[NSWorkspace sharedWorkspace] openURL: o_url];
2056 }
2057
2058 - (IBAction)openDonate:(id)sender
2059 {
2060     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/contribute.html#paypal"];
2061
2062     [[NSWorkspace sharedWorkspace] openURL: o_url];
2063 }
2064
2065 - (IBAction)openCrashLog:(id)sender
2066 {
2067     NSString * o_path = [@"~/Library/Logs/CrashReporter/VLC.crash.log"
2068                                     stringByExpandingTildeInPath];
2069
2070
2071     if( [[NSFileManager defaultManager] fileExistsAtPath: o_path ] )
2072     {
2073         [[NSWorkspace sharedWorkspace] openFile: o_path
2074                                     withApplication: @"Console"];
2075     }
2076     else
2077     {
2078         NSBeginInformationalAlertSheet(_NS("No CrashLog found"), _NS("Continue"), nil, nil, o_msgs_panel, self, NULL, NULL, nil, _NS("Couldn't find any trace of a previous crash.") );
2079
2080     }
2081 }
2082
2083 - (IBAction)viewErrorsAndWarnings:(id)sender
2084 {
2085     [[[self getInteractionList] getErrorPanel] showPanel];
2086 }
2087
2088 - (IBAction)showMessagesPanel:(id)sender
2089 {
2090     [o_msgs_panel makeKeyAndOrderFront: sender];
2091 }
2092
2093 - (IBAction)showInformationPanel:(id)sender
2094 {
2095     if(! nib_info_loaded )
2096         nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: self];
2097     
2098     [o_info initPanel];
2099 }
2100
2101 - (void)windowDidBecomeKey:(NSNotification *)o_notification
2102 {
2103     if( [o_notification object] == o_msgs_panel )
2104     {
2105         id o_msg;
2106         NSEnumerator * o_enum;
2107
2108         [o_messages setString: @""];
2109
2110         [o_msg_lock lock];
2111
2112         o_enum = [o_msg_arr objectEnumerator];
2113
2114         while( ( o_msg = [o_enum nextObject] ) != nil )
2115         {
2116             [o_messages insertText: o_msg];
2117         }
2118
2119         [o_msg_lock unlock];
2120     }
2121 }
2122
2123 - (IBAction)togglePlaylist:(id)sender
2124 {
2125     NSRect o_rect = [o_window frame];
2126     /*First, check if the playlist is visible*/
2127     if( o_rect.size.height <= 200 )
2128     {
2129         o_restore_rect = o_rect;
2130         b_restore_size = true;
2131         b_small_window = YES; /* we know we are small, make sure this is actually set (see case below) */
2132         /* make large */
2133         if( o_size_with_playlist.height > 200 )
2134         {
2135             o_rect.size.height = o_size_with_playlist.height;
2136         } else {
2137             o_rect.size.height = 500;
2138         }
2139  
2140         if( o_size_with_playlist.width > [o_window minSize].width )
2141         {
2142             o_rect.size.width = o_size_with_playlist.width;
2143         } else {
2144             o_rect.size.width = 500;
2145         }
2146  
2147         o_rect.size.height = (o_size_with_playlist.height > 200) ?
2148             o_size_with_playlist.height : 500;
2149         o_rect.origin.x = [o_window frame].origin.x;
2150         o_rect.origin.y = [o_window frame].origin.y - o_rect.size.height +
2151                                                 [o_window minSize].height;
2152
2153         NSRect screenRect = [[o_window screen] visibleFrame];
2154         if( !NSContainsRect( screenRect, o_rect ) ) {
2155             if( NSMaxX(o_rect) > NSMaxX(screenRect) )
2156                 o_rect.origin.x = ( NSMaxX(screenRect) - o_rect.size.width );
2157             if( NSMinY(o_rect) < NSMinY(screenRect) )
2158                 o_rect.origin.y = ( NSMinY(screenRect) );
2159         }
2160
2161         [o_btn_playlist setState: YES];
2162     }
2163     else
2164     {
2165         NSSize curSize = o_rect.size;
2166         /* make small */
2167         o_rect.size.height = [o_window minSize].height;
2168         o_rect.size.width = [o_window minSize].width;
2169         o_rect.origin.x = [o_window frame].origin.x;
2170         /* Calculate the position of the lower right corner after resize */
2171         o_rect.origin.y = [o_window frame].origin.y +
2172             [o_window frame].size.height - [o_window minSize].height;
2173
2174         if( b_restore_size )
2175             o_rect = o_restore_rect;
2176
2177         [o_playlist_view setAutoresizesSubviews: NO];
2178         [o_playlist_view removeFromSuperview];
2179         [o_btn_playlist setState: NO];
2180         b_small_window = NO; /* we aren't small here just yet. we are doing an animated resize after this */
2181     }
2182
2183     [o_window setFrame: o_rect display:YES animate: YES];
2184 }
2185
2186 - (void)updateTogglePlaylistState
2187 {
2188     if( [o_window frame].size.height <= 200 )
2189     {
2190         [o_btn_playlist setState: NO];
2191     }
2192     else
2193     {
2194         [o_btn_playlist setState: YES];
2195     }
2196 }
2197
2198 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
2199 {
2200     /* Not triggered on a window resize or maxification of the window. only by window mouse dragging resize */
2201
2202    /*Stores the size the controller one resize, to be able to restore it when
2203      toggling the playlist*/
2204     o_size_with_playlist = proposedFrameSize;
2205
2206     if( proposedFrameSize.height <= 200 )
2207     {
2208         if( b_small_window == NO )
2209         {
2210             /* if large and going to small then hide */
2211             b_small_window = YES;
2212             [o_playlist_view setAutoresizesSubviews: NO];
2213             [o_playlist_view removeFromSuperview];
2214         }
2215         return NSMakeSize( proposedFrameSize.width, [o_window minSize].height);
2216     }
2217     return proposedFrameSize;
2218 }
2219
2220 - (void)windowDidMove:(NSNotification *)notif
2221 {
2222     b_restore_size = false;
2223 }
2224
2225 - (void)windowDidResize:(NSNotification *)notif
2226 {
2227     if( [o_window frame].size.height > 200 && b_small_window )
2228     {
2229         /* If large and coming from small then show */
2230         [o_playlist_view setAutoresizesSubviews: YES];
2231         [o_playlist_view setFrame: NSMakeRect( 10, 10, [o_window frame].size.width - 20, [o_window frame].size.height - [o_window minSize].height - 10 )];
2232         [o_playlist_view setNeedsDisplay:YES];
2233         [[o_window contentView] addSubview: o_playlist_view];
2234         b_small_window = NO;
2235     }
2236     [self updateTogglePlaylistState];
2237 }
2238
2239 @end
2240
2241 @implementation VLCMain (NSMenuValidation)
2242
2243 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
2244 {
2245     NSString *o_title = [o_mi title];
2246     BOOL bEnabled = TRUE;
2247
2248     /* Recent Items Menu */
2249     if( [o_title isEqualToString: _NS("Clear Menu")] )
2250     {
2251         NSMenu * o_menu = [o_mi_open_recent submenu];
2252         int i_nb_items = [o_menu numberOfItems];
2253         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
2254                                                        recentDocumentURLs];
2255         UInt32 i_nb_docs = [o_docs count];
2256
2257         if( i_nb_items > 1 )
2258         {
2259             while( --i_nb_items )
2260             {
2261                 [o_menu removeItemAtIndex: 0];
2262             }
2263         }
2264
2265         if( i_nb_docs > 0 )
2266         {
2267             NSURL * o_url;
2268             NSString * o_doc;
2269
2270             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
2271
2272             while( TRUE )
2273             {
2274                 i_nb_docs--;
2275
2276                 o_url = [o_docs objectAtIndex: i_nb_docs];
2277
2278                 if( [o_url isFileURL] )
2279                 {
2280                     o_doc = [o_url path];
2281                 }
2282                 else
2283                 {
2284                     o_doc = [o_url absoluteString];
2285                 }
2286
2287                 [o_menu insertItemWithTitle: o_doc
2288                     action: @selector(openRecentItem:)
2289                     keyEquivalent: @"" atIndex: 0];
2290
2291                 if( i_nb_docs == 0 )
2292                 {
2293                     break;
2294                 }
2295             }
2296         }
2297         else
2298         {
2299             bEnabled = FALSE;
2300         }
2301     }
2302     return( bEnabled );
2303 }
2304
2305 @end
2306
2307 @implementation VLCMain (Internal)
2308
2309 - (void)handlePortMessage:(NSPortMessage *)o_msg
2310 {
2311     id ** val;
2312     NSData * o_data;
2313     NSValue * o_value;
2314     NSInvocation * o_inv;
2315     NSConditionLock * o_lock;
2316
2317     o_data = [[o_msg components] lastObject];
2318     o_inv = *((NSInvocation **)[o_data bytes]);
2319     [o_inv getArgument: &o_value atIndex: 2];
2320     val = (id **)[o_value pointerValue];
2321     [o_inv setArgument: val[1] atIndex: 2];
2322     o_lock = *(val[0]);
2323
2324     [o_lock lock];
2325     [o_inv invoke];
2326     [o_lock unlockWithCondition: 1];
2327 }
2328
2329 @end