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