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