]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
macosx: move VLCFSPanel from VLCControls to VLCMainWindow since its the only class...
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2011 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_common.h>
34 #include <vlc_keys.h>
35 #include <vlc_dialog.h>
36 #include <vlc_url.h>
37 #include <vlc_modules.h>
38 #include <vlc_aout_intf.h>
39 #include <vlc_vout_window.h>
40 #include <unistd.h> /* execl() */
41
42 #import "intf.h"
43 #import "MainMenu.h"
44 #import "MainWindow.h"
45 #import "vout.h"
46 #import "prefs.h"
47 #import "playlist.h"
48 #import "playlistinfo.h"
49 #import "controls.h"
50 #import "open.h"
51 #import "wizard.h"
52 #import "bookmarks.h"
53 #import "coredialogs.h"
54 #import "embeddedwindow.h"
55 #import "AppleRemote.h"
56 #import "eyetv.h"
57 #import "simple_prefs.h"
58 #import "CoreInteraction.h"
59 #import "TrackSynchronization.h"
60
61 #import <AddressBook/AddressBook.h>         /* for crashlog send mechanism */
62 #import <Sparkle/Sparkle.h>                 /* we're the update delegate */
63
64 /*****************************************************************************
65  * Local prototypes.
66  *****************************************************************************/
67 static void Run ( intf_thread_t *p_intf );
68
69 static void updateProgressPanel (void *, const char *, float);
70 static bool checkProgressPanel (void *);
71 static void destroyProgressPanel (void *);
72
73 static void MsgCallback( msg_cb_data_t *, const msg_item_t * );
74
75 static int InputEvent( vlc_object_t *, const char *,
76                       vlc_value_t, vlc_value_t, void * );
77 static int PLItemChanged( vlc_object_t *, const char *,
78                          vlc_value_t, vlc_value_t, void * );
79 static int PlaylistUpdated( vlc_object_t *, const char *,
80                            vlc_value_t, vlc_value_t, void * );
81 static int PlaybackModeUpdated( vlc_object_t *, const char *,
82                                vlc_value_t, vlc_value_t, void * );
83 static int VolumeUpdated( vlc_object_t *, const char *,
84                          vlc_value_t, vlc_value_t, void * );
85
86 #pragma mark -
87 #pragma mark VLC Interface Object Callbacks
88
89 /*****************************************************************************
90  * OpenIntf: initialize interface
91  *****************************************************************************/
92 int OpenIntf ( vlc_object_t *p_this )
93 {
94     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
95     [VLCApplication sharedApplication];
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         return VLC_ENOMEM;
101
102     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
103
104     /* subscribe to LibVLCCore's messages */
105     p_intf->p_sys->p_sub = msg_Subscribe( p_intf->p_libvlc, MsgCallback, NULL );
106     p_intf->pf_run = Run;
107     p_intf->b_should_run_on_first_thread = true;
108
109     [o_pool release];
110     return VLC_SUCCESS;
111 }
112
113 /*****************************************************************************
114  * CloseIntf: destroy interface
115  *****************************************************************************/
116 void CloseIntf ( vlc_object_t *p_this )
117 {
118     intf_thread_t *p_intf = (intf_thread_t*) p_this;
119
120     free( p_intf->p_sys );
121 }
122
123 static int WindowControl( vout_window_t *, int i_query, va_list );
124
125 int WindowOpen( vout_window_t *p_wnd, const vout_window_cfg_t *cfg )
126 {
127     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
128     intf_thread_t *p_intf = VLCIntf;
129     if (!p_intf) {
130         msg_Err( p_wnd, "Mac OS X interface not found" );
131         return VLC_EGENERIC;
132     }
133
134     int i_x = cfg->x;
135     int i_y = cfg->y;
136     unsigned i_width = cfg->width;
137     unsigned i_height = cfg->height;
138     p_wnd->handle.nsobject = [[VLCMain sharedInstance] getVideoViewAtPositionX: &i_x Y: &i_y withWidth: &i_width andHeight: &i_height];
139
140     if ( !p_wnd->handle.nsobject ) {
141         msg_Err( p_wnd, "got no video view from the interface" );
142         [o_pool release];
143         return VLC_EGENERIC;
144     }
145
146     [[VLCMain sharedInstance] setNativeVideoSize:NSMakeSize( cfg->width, cfg->height )];
147     [[VLCMain sharedInstance] setActiveVideoPlayback: YES];
148     p_wnd->control = WindowControl;
149     p_wnd->sys = (vout_window_sys_t *)VLCIntf;
150     [o_pool release];
151     return VLC_SUCCESS;
152 }
153
154 static int WindowControl( vout_window_t *p_wnd, int i_query, va_list args )
155 {
156     /* TODO */
157     if( i_query == VOUT_WINDOW_SET_STATE )
158         NSLog( @"WindowControl:VOUT_WINDOW_SET_STATE" );
159     else if( i_query == VOUT_WINDOW_SET_SIZE )
160     {
161         NSLog( @"WindowControl:VOUT_WINDOW_SET_SIZE" );
162         unsigned int i_width  = va_arg( args, unsigned int );
163         unsigned int i_height = va_arg( args, unsigned int );
164         [[VLCMain sharedInstance] setNativeVideoSize:NSMakeSize( i_width, i_height )];
165     }
166     else if( i_query == VOUT_WINDOW_SET_FULLSCREEN )
167         NSLog( @"WindowControl:VOUT_WINDOW_SET_FULLSCREEN" );
168     else
169         NSLog( @"WindowControl: unknown query" );
170     return VLC_SUCCESS;
171 }
172
173 void WindowClose( vout_window_t *p_wnd )
174 {
175     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
176     [[VLCMain sharedInstance] setActiveVideoPlayback:NO];
177     NSLog( @"Window Close" );
178     // tell the interface to get rid of the video, TODO
179     [o_pool release];
180 }
181
182 /*****************************************************************************
183  * Run: main loop
184  *****************************************************************************/
185 static NSLock * o_appLock = nil;    // controls access to f_appExit
186 static int f_appExit = 0;           // set to 1 when application termination signaled
187
188 static void Run( intf_thread_t *p_intf )
189 {
190     sigset_t set;
191
192     /* Make sure the "force quit" menu item does quit instantly.
193      * VLC overrides SIGTERM which is sent by the "force quit"
194      * menu item to make sure daemon mode quits gracefully, so
195      * we un-override SIGTERM here. */
196     sigemptyset( &set );
197     sigaddset( &set, SIGTERM );
198     pthread_sigmask( SIG_UNBLOCK, &set, NULL );
199
200     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
201     [VLCApplication sharedApplication];
202
203     o_appLock = [[NSLock alloc] init];
204
205     [[VLCMain sharedInstance] setIntf: p_intf];
206     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
207
208     [NSApp run];
209     [[VLCMain sharedInstance] applicationWillTerminate:nil];
210     [o_appLock release];
211     [o_pool release];
212 }
213
214 #pragma mark -
215 #pragma mark Variables Callback
216
217 /*****************************************************************************
218  * MsgCallback: Callback triggered by the core once a new debug message is
219  * ready to be displayed. We store everything in a NSArray in our Cocoa part
220  * of this file.
221  *****************************************************************************/
222 static void MsgCallback( msg_cb_data_t *data, const msg_item_t *item )
223 {
224     int canc = vlc_savecancel();
225
226     /* this may happen from time to time, let's bail out as info would be useless anyway */
227     if( !item->psz_module || !item->psz_msg )
228         return;
229
230     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
231     [[VLCMain sharedInstance] processReceivedlibvlcMessage: item];
232     [o_pool release];
233
234     vlc_restorecancel( canc );
235 }
236
237 static int InputEvent( vlc_object_t *p_this, const char *psz_var,
238                        vlc_value_t oldval, vlc_value_t new_val, void *param )
239 {
240     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
241     switch (new_val.i_int) {
242         case INPUT_EVENT_STATE:
243             [[VLCMain sharedInstance] playbackStatusUpdated];
244             break;
245         case INPUT_EVENT_RATE:
246             [[VLCMainMenu sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
247             break;
248         case INPUT_EVENT_POSITION:
249             [[VLCMain sharedInstance] updatePlaybackPosition];
250             break;
251         case INPUT_EVENT_TITLE:
252         case INPUT_EVENT_CHAPTER:
253             break;
254         case INPUT_EVENT_CACHE:
255             [[VLCMain sharedInstance] updateMainWindow];
256             break;
257         case INPUT_EVENT_STATISTICS:
258             [[[VLCMain sharedInstance] info] performSelectorOnMainThread:@selector(updateStatistics) withObject: nil waitUntilDone: NO];
259             break;
260         case INPUT_EVENT_ES:
261             break;
262         case INPUT_EVENT_TELETEXT:
263             NSLog( @"teletext" );
264             break;
265         case INPUT_EVENT_AOUT:
266             break;
267         case INPUT_EVENT_VOUT:
268             break;
269         case INPUT_EVENT_ITEM_META:
270         case INPUT_EVENT_ITEM_INFO:
271             [[VLCMain sharedInstance] updateName];
272             [[VLCMain sharedInstance] updateInfoandMetaPanel];
273             break;
274         case INPUT_EVENT_BOOKMARK:
275             break;
276         case INPUT_EVENT_RECORD:
277             break;
278         case INPUT_EVENT_PROGRAM:
279             break;
280         case INPUT_EVENT_ITEM_EPG:
281             break;
282         case INPUT_EVENT_SIGNAL:
283             break;
284
285         case INPUT_EVENT_ITEM_NAME:
286             [[VLCMain sharedInstance] updateName];
287             [[VLCMain sharedInstance] playlistUpdated];
288             break;
289
290         case INPUT_EVENT_AUDIO_DELAY:
291         case INPUT_EVENT_SUBTITLE_DELAY:
292             [[VLCMain sharedInstance] updateDelays];
293             break;
294
295         case INPUT_EVENT_DEAD:
296             [[VLCMain sharedInstance] updateName];
297             [[VLCMain sharedInstance] updatePlaybackPosition];
298             break;
299
300         case INPUT_EVENT_ABORT:
301             //NSLog( @"input stopped by user" );
302             break;
303
304         default:
305             //msg_Warn( p_this, "unhandled input event (%lld)", new_val.i_int );
306             break;
307     }
308
309     [o_pool release];
310     return VLC_SUCCESS;
311 }
312
313 static int PLItemChanged( vlc_object_t *p_this, const char *psz_var,
314                          vlc_value_t oldval, vlc_value_t new_val, void *param )
315 {
316     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
317     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO];
318
319     [o_pool release];
320     return VLC_SUCCESS;
321 }
322
323 static int PlaylistUpdated( vlc_object_t *p_this, const char *psz_var,
324                          vlc_value_t oldval, vlc_value_t new_val, void *param )
325 {
326     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
327     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playlistUpdated) withObject:nil waitUntilDone:NO];
328
329     [o_pool release];
330     return VLC_SUCCESS;
331 }
332
333 static int PlaybackModeUpdated( vlc_object_t *p_this, const char *psz_var,
334                          vlc_value_t oldval, vlc_value_t new_val, void *param )
335 {
336     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
337     [[VLCMain sharedInstance] playbackModeUpdated];
338
339     [o_pool release];
340     return VLC_SUCCESS;
341 }
342
343 static int VolumeUpdated( vlc_object_t *p_this, const char *psz_var,
344                          vlc_value_t oldval, vlc_value_t new_val, void *param )
345 {
346     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
347     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateVolume) withObject:nil waitUntilDone:NO];
348
349     [o_pool release];
350     return VLC_SUCCESS;
351 }
352
353 /*****************************************************************************
354  * ShowController: Callback triggered by the show-intf playlist variable
355  * through the ShowIntf-control-intf, to let us show the controller-win;
356  * usually when in fullscreen-mode
357  *****************************************************************************/
358 static int ShowController( vlc_object_t *p_this, const char *psz_variable,
359                      vlc_value_t old_val, vlc_value_t new_val, void *param )
360 {
361     intf_thread_t * p_intf = VLCIntf;
362     if( p_intf && p_intf->p_sys )
363     {
364 //        [[[VLCMain sharedInstance] fspanel] makeKeyAndOrderFront: nil];
365     }
366     return VLC_SUCCESS;
367 }
368
369 /*****************************************************************************
370  * FullscreenChanged: Callback triggered by the fullscreen-change playlist
371  * variable, to let the intf update the controller.
372  *****************************************************************************/
373 static int FullscreenChanged( vlc_object_t *p_this, const char *psz_variable,
374                      vlc_value_t old_val, vlc_value_t new_val, void *param )
375 {
376     intf_thread_t * p_intf = VLCIntf;
377     if (p_intf)
378     {
379         NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
380         [[VLCMain sharedInstance] fullscreenChanged];
381         [o_pool release];
382     }
383     return VLC_SUCCESS;
384 }
385
386 /*****************************************************************************
387  * DialogCallback: Callback triggered by the "dialog-*" variables
388  * to let the intf display error and interaction dialogs
389  *****************************************************************************/
390 static int DialogCallback( vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data )
391 {
392     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
393     VLCMain *interface = (VLCMain *)data;
394
395     if( [[NSString stringWithUTF8String: type] isEqualToString: @"dialog-progress-bar"] )
396     {
397         /* the progress panel needs to update itself and therefore wants special treatment within this context */
398         dialog_progress_bar_t *p_dialog = (dialog_progress_bar_t *)value.p_address;
399
400         p_dialog->pf_update = updateProgressPanel;
401         p_dialog->pf_check = checkProgressPanel;
402         p_dialog->pf_destroy = destroyProgressPanel;
403         p_dialog->p_sys = VLCIntf->p_libvlc;
404     }
405
406     NSValue *o_value = [NSValue valueWithPointer:value.p_address];
407     [[VLCCoreDialogProvider sharedInstance] performEventWithObject: o_value ofType: type];
408
409     [o_pool release];
410     return VLC_SUCCESS;
411 }
412
413 void updateProgressPanel (void *priv, const char *text, float value)
414 {
415     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
416
417     NSString *o_txt;
418     if( text != NULL )
419         o_txt = [NSString stringWithUTF8String: text];
420     else
421         o_txt = @"";
422
423     [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)];
424
425     [o_pool release];
426 }
427
428 void destroyProgressPanel (void *priv)
429 {
430     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
431     [[[VLCMain sharedInstance] coreDialogProvider] destroyProgressPanel];
432     [o_pool release];
433 }
434
435 bool checkProgressPanel (void *priv)
436 {
437     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
438     return [[[VLCMain sharedInstance] coreDialogProvider] progressCancelled];
439     [o_pool release];
440 }
441
442 #pragma mark -
443 #pragma mark Helpers
444
445 input_thread_t *getInput(void)
446 {
447     intf_thread_t *p_intf = VLCIntf;
448     if (!p_intf)
449         return NULL;
450     return pl_CurrentInput(p_intf);
451 }
452
453 vout_thread_t *getVout(void)
454 {
455     input_thread_t *p_input = getInput();
456     if (!p_input)
457         return NULL;
458     vout_thread_t *p_vout = input_GetVout(p_input);
459     vlc_object_release(p_input);
460     return p_vout;
461 }
462
463 audio_output_t *getAout(void)
464 {
465     input_thread_t *p_input = getInput();
466     if (!p_input)
467         return NULL;
468     audio_output_t *p_aout = input_GetAout(p_input);
469     vlc_object_release(p_input);
470     return p_aout;
471 }
472
473 #pragma mark -
474 #pragma mark Private
475
476 @interface VLCMain ()
477 - (void)_removeOldPreferences;
478 @end
479
480 /*****************************************************************************
481  * VLCMain implementation
482  *****************************************************************************/
483 @implementation VLCMain
484
485 #pragma mark -
486 #pragma mark Initialization
487
488 static VLCMain *_o_sharedMainInstance = nil;
489
490 + (VLCMain *)sharedInstance
491 {
492     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
493 }
494
495 - (id)init
496 {
497     if( _o_sharedMainInstance)
498     {
499         [self dealloc];
500         return _o_sharedMainInstance;
501     }
502     else
503         _o_sharedMainInstance = [super init];
504
505     p_intf = NULL;
506
507     o_msg_lock = [[NSLock alloc] init];
508     o_msg_arr = [[NSMutableArray arrayWithCapacity: 600] retain];
509
510     o_open = [[VLCOpen alloc] init];
511     //o_embedded_list = [[VLCEmbeddedList alloc] init];
512     o_coredialogs = [[VLCCoreDialogProvider alloc] init];
513     o_info = [[VLCInfo alloc] init];
514     o_mainmenu = [[VLCMainMenu alloc] init];
515     o_coreinteraction = [[VLCCoreInteraction alloc] init];
516     o_eyetv = [[VLCEyeTVController alloc] init];
517     o_mainwindow = [[VLCMainWindow alloc] init];
518
519     /* announce our launch to a potential eyetv plugin */
520     [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
521                                                                    object: @"VLCEyeTVSupport"
522                                                                  userInfo: NULL
523                                                        deliverImmediately: YES];
524     return _o_sharedMainInstance;
525 }
526
527 - (void)setIntf: (intf_thread_t *)p_mainintf {
528     p_intf = p_mainintf;
529 }
530
531 - (intf_thread_t *)intf {
532     return p_intf;
533 }
534
535 - (void)awakeFromNib
536 {
537     playlist_t *p_playlist;
538     vlc_value_t val;
539     if( !p_intf ) return;
540     var_Create( p_intf, "intf-change", VLC_VAR_BOOL );
541
542     /* Check if we already did this once. Opening the other nibs calls it too,
543      because VLCMain is the owner */
544     if( nib_main_loaded ) return;
545
546     [o_msgs_panel setExcludedFromWindowsMenu: YES];
547     [o_msgs_panel setDelegate: self];
548
549     p_playlist = pl_Get( p_intf );
550
551     val.b_bool = false;
552
553     var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
554     var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
555 //    var_AddCallback(p_playlist, "item-change", PLItemChanged, self);
556     var_AddCallback(p_playlist, "item-current", PLItemChanged, self);
557     var_AddCallback(p_playlist, "activity", PLItemChanged, self);
558     var_AddCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self);
559     var_AddCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self);
560     var_AddCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self);
561     var_AddCallback(p_playlist, "random", PlaybackModeUpdated, self);
562     var_AddCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
563     var_AddCallback(p_playlist, "loop", PlaybackModeUpdated, self);
564     var_AddCallback(p_playlist, "volume", VolumeUpdated, self);
565     var_AddCallback(p_playlist, "mute", VolumeUpdated, self);
566
567     /* load our Core Dialogs nib */
568     nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: NSApp];
569
570     /* subscribe to various interactive dialogues */
571     var_Create( p_intf, "dialog-error", VLC_VAR_ADDRESS );
572     var_AddCallback( p_intf, "dialog-error", DialogCallback, self );
573     var_Create( p_intf, "dialog-critical", VLC_VAR_ADDRESS );
574     var_AddCallback( p_intf, "dialog-critical", DialogCallback, self );
575     var_Create( p_intf, "dialog-login", VLC_VAR_ADDRESS );
576     var_AddCallback( p_intf, "dialog-login", DialogCallback, self );
577     var_Create( p_intf, "dialog-question", VLC_VAR_ADDRESS );
578     var_AddCallback( p_intf, "dialog-question", DialogCallback, self );
579     var_Create( p_intf, "dialog-progress-bar", VLC_VAR_ADDRESS );
580     var_AddCallback( p_intf, "dialog-progress-bar", DialogCallback, self );
581     dialog_Register( p_intf );
582
583     [self playbackModeUpdated];
584
585     /* init Apple Remote support */
586     o_remote = [[AppleRemote alloc] init];
587     [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
588     [o_remote setDelegate: _o_sharedMainInstance];
589
590     /* yeah, we are done */
591     nib_main_loaded = TRUE;
592 }
593
594 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
595 {
596     if( !p_intf ) return;
597
598     /* init media key support */
599     b_mediaKeySupport = config_GetInt( VLCIntf, "macosx-mediakeys" );
600     if( b_mediaKeySupport )
601     {
602         o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
603         [o_mediaKeyController startWatchingMediaKeys];
604         [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
605                                                                  [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
606                                                                  nil]];
607     }
608     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name: @"VLCMediaKeySupportSettingChanged" object: nil];
609
610     [self _removeOldPreferences];
611
612     [o_mainwindow makeKeyAndOrderFront: self];
613
614     /* Handle sleep notification */
615     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
616            name:NSWorkspaceWillSleepNotification object:nil];
617
618     [NSThread detachNewThreadSelector:@selector(lookForCrashLog) toTarget:self withObject:nil];
619 }
620
621 - (void)initStrings
622 {
623     if( !p_intf ) return;
624
625     /* messages panel */
626     [o_msgs_panel setTitle: _NS("Messages")];
627     [o_msgs_crashlog_btn setTitle: _NS("Open CrashLog...")];
628     [o_msgs_save_btn setTitle: _NS("Save this Log...")];
629
630     /* crash reporter panel */
631     [o_crashrep_send_btn setTitle: _NS("Send")];
632     [o_crashrep_dontSend_btn setTitle: _NS("Don't Send")];
633     [o_crashrep_title_txt setStringValue: _NS("VLC crashed previously")];
634     [o_crashrep_win setTitle: _NS("VLC crashed previously")];
635     [o_crashrep_desc_txt setStringValue: _NS("Do you want to send details on the crash to VLC's development team?\n\nIf you want, you can enter a few lines on what you did before VLC crashed along with other helpful information: a link to download a sample file, a URL of a network stream, ...")];
636     [o_crashrep_includeEmail_ckb setTitle: _NS("I agree to be possibly contacted about this bugreport.")];
637     [o_crashrep_includeEmail_txt setStringValue: _NS("Only your default E-Mail address will be submitted, including no further information.")];
638 }
639
640 #pragma mark -
641 #pragma mark Termination
642
643 - (void)applicationWillTerminate:(NSNotification *)notification
644 {
645     playlist_t * p_playlist;
646     vout_thread_t * p_vout;
647     int returnedValue = 0;
648
649     if( !p_intf )
650         return;
651
652     // don't allow a double termination call. If the user has
653     // already invoked the quit then simply return this time.
654     int isTerminating = false;
655
656     [o_appLock lock];
657     isTerminating = (f_appExit++ > 0 ? 1 : 0);
658     [o_appLock unlock];
659
660     if (isTerminating)
661         return;
662
663     msg_Dbg( p_intf, "Terminating" );
664
665     /* Make sure the intf object is getting killed */
666     vlc_object_kill( p_intf );
667     p_playlist = pl_Get( p_intf );
668
669     /* unsubscribe from the interactive dialogues */
670     dialog_Unregister( p_intf );
671     var_DelCallback( p_intf, "dialog-error", DialogCallback, self );
672     var_DelCallback( p_intf, "dialog-critical", DialogCallback, self );
673     var_DelCallback( p_intf, "dialog-login", DialogCallback, self );
674     var_DelCallback( p_intf, "dialog-question", DialogCallback, self );
675     var_DelCallback( p_intf, "dialog-progress-bar", DialogCallback, self );
676     //var_DelCallback(p_playlist, "item-change", PLItemChanged, self);
677     var_DelCallback(p_playlist, "item-current", PLItemChanged, self);
678     var_DelCallback(p_playlist, "activity", PLItemChanged, self);
679     var_DelCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self);
680     var_DelCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self);
681     var_DelCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self);
682     var_DelCallback(p_playlist, "random", PlaybackModeUpdated, self);
683     var_DelCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
684     var_DelCallback(p_playlist, "loop", PlaybackModeUpdated, self);
685     var_DelCallback(p_playlist, "volume", VolumeUpdated, self);
686     var_DelCallback(p_playlist, "mute", VolumeUpdated, self);
687
688     /* remove global observer watching for vout device changes correctly */
689     [[NSNotificationCenter defaultCenter] removeObserver: self];
690
691     /* release some other objects here, because it isn't sure whether dealloc
692      * will be called later on */
693     if( o_sprefs )
694         [o_sprefs release];
695
696     if( o_prefs )
697         [o_prefs release];
698
699     [o_open release];
700
701     if( o_info )
702         [o_info release];
703
704     if( o_wizard )
705         [o_wizard release];
706
707     [crashLogURLConnection cancel];
708     [crashLogURLConnection release];
709
710     [o_embedded_list release];
711     [o_coredialogs release];
712     [o_eyetv release];
713     [o_mainwindow release];
714
715     /* unsubscribe from libvlc's debug messages */
716     msg_Unsubscribe( p_intf->p_sys->p_sub );
717
718     [o_msg_arr removeAllObjects];
719     [o_msg_arr release];
720
721     [o_msg_lock release];
722
723     /* write cached user defaults to disk */
724     [[NSUserDefaults standardUserDefaults] synchronize];
725
726     /* Make sure the Menu doesn't have any references to vlc objects anymore */
727     //FIXME: this should be moved to VLCMainMenu
728     [o_mainmenu releaseRepresentedObjects:[NSApp mainMenu]];
729     [o_mainmenu release];
730
731     /* Kill the playlist, so that it doesn't accept new request
732      * such as the play request from vlc.c (we are a blocking interface). */
733     vlc_object_kill( p_playlist );
734     libvlc_Quit( p_intf->p_libvlc );
735
736     [self setIntf:nil];
737 }
738
739 #pragma mark -
740 #pragma mark Sparkle delegate
741 /* received directly before the update gets installed, so let's shut down a bit */
742 - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
743 {
744     [NSApp activateIgnoringOtherApps:YES];
745     [o_remote stopListening: self];
746     var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_STOP );
747
748     /* Close the window directly, because we do know that there
749      * won't be anymore video. It's currently waiting a bit. */
750     [[[o_coreinteraction voutView] window] orderOut:self];
751 }
752
753 #pragma mark -
754 #pragma mark Media Key support
755
756 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
757 {
758     if( b_mediaKeySupport )
759        {
760         assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
761
762         int keyCode = (([event data1] & 0xFFFF0000) >> 16);
763         int keyFlags = ([event data1] & 0x0000FFFF);
764         int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
765         int keyRepeat = (keyFlags & 0x1);
766
767         if( keyCode == NX_KEYTYPE_PLAY && keyState == 0 )
768             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PLAY_PAUSE );
769
770         if( keyCode == NX_KEYTYPE_FAST && !b_mediakeyJustJumped )
771         {
772             if( keyState == 0 && keyRepeat == 0 )
773                 var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_NEXT );
774             else if( keyRepeat == 1 )
775             {
776                 var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_FORWARD_SHORT );
777                 b_mediakeyJustJumped = YES;
778                 [self performSelector:@selector(resetMediaKeyJump)
779                            withObject: NULL
780                            afterDelay:0.25];
781             }
782         }
783
784         if( keyCode == NX_KEYTYPE_REWIND && !b_mediakeyJustJumped )
785         {
786             if( keyState == 0 && keyRepeat == 0 )
787                 var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PREV );
788             else if( keyRepeat == 1 )
789             {
790                 var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_BACKWARD_SHORT );
791                 b_mediakeyJustJumped = YES;
792                 [self performSelector:@selector(resetMediaKeyJump)
793                            withObject: NULL
794                            afterDelay:0.25];
795             }
796         }
797     }
798 }
799
800 #pragma mark -
801 #pragma mark Other notification
802
803 /* Listen to the remote in exclusive mode, only when VLC is the active
804    application */
805 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
806 {
807     if( !p_intf ) return;
808         if( config_GetInt( p_intf, "macosx-appleremote" ) == YES )
809                 [o_remote startListening: self];
810 }
811 - (void)applicationDidResignActive:(NSNotification *)aNotification
812 {
813     if( !p_intf ) return;
814     [o_remote stopListening: self];
815 }
816
817 /* Triggered when the computer goes to sleep */
818 - (void)computerWillSleep: (NSNotification *)notification
819 {
820     input_thread_t * p_input;
821
822     p_input = pl_CurrentInput( p_intf );
823     if( p_input )
824     {
825         int state = var_GetInteger( p_input, "state" );
826         if( state == PLAYING_S )
827             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PLAY_PAUSE );
828         vlc_object_release( p_input );
829     }
830 }
831
832 #pragma mark -
833 #pragma mark File opening
834
835 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
836 {
837     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
838     char *psz_uri = make_URI([o_filename UTF8String], "file" );
839     if( !psz_uri )
840         return( FALSE );
841
842     NSDictionary *o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
843
844     free( psz_uri );
845
846     if( b_autoplay )
847         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
848     else
849         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: YES];
850
851     return( TRUE );
852 }
853
854 /* When user click in the Dock icon our double click in the finder */
855 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
856 {
857     if(!hasVisibleWindows)
858         [o_mainwindow makeKeyAndOrderFront:self];
859
860     return YES;
861 }
862
863 #pragma mark -
864 #pragma mark Apple Remote Control
865
866 /* Helper method for the remote control interface in order to trigger forward/backward and volume
867    increase/decrease as long as the user holds the left/right, plus/minus button */
868 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
869 {
870     if(b_remote_button_hold)
871     {
872         switch([buttonIdentifierNumber intValue])
873         {
874             case kRemoteButtonRight_Hold:
875                 [[VLCCoreInteraction sharedInstance] forward];
876             break;
877             case kRemoteButtonLeft_Hold:
878                 [[VLCCoreInteraction sharedInstance] backward];
879             break;
880             case kRemoteButtonVolume_Plus_Hold:
881                 [[VLCCoreInteraction sharedInstance] volumeUp];
882             break;
883             case kRemoteButtonVolume_Minus_Hold:
884                 [[VLCCoreInteraction sharedInstance] volumeDown];
885             break;
886         }
887         if(b_remote_button_hold)
888         {
889             /* trigger event */
890             [self performSelector:@selector(executeHoldActionForRemoteButton:)
891                          withObject:buttonIdentifierNumber
892                          afterDelay:0.25];
893         }
894     }
895 }
896
897 /* Apple Remote callback */
898 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
899                pressedDown: (BOOL) pressedDown
900                 clickCount: (unsigned int) count
901 {
902     switch( buttonIdentifier )
903     {
904         case k2009RemoteButtonFullscreen:
905             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
906             break;
907         case k2009RemoteButtonPlay:
908             [[VLCCoreInteraction sharedInstance] play];
909             break;
910         case kRemoteButtonPlay:
911             if(count >= 2) {
912                 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
913             } else {
914                 [[VLCCoreInteraction sharedInstance] play];
915             }
916             break;
917         case kRemoteButtonVolume_Plus:
918             [[VLCCoreInteraction sharedInstance] volumeUp];
919             break;
920         case kRemoteButtonVolume_Minus:
921             [[VLCCoreInteraction sharedInstance] volumeDown];
922             break;
923         case kRemoteButtonRight:
924             [[VLCCoreInteraction sharedInstance] next];
925             break;
926         case kRemoteButtonLeft:
927             [[VLCCoreInteraction sharedInstance] previous];
928             break;
929         case kRemoteButtonRight_Hold:
930         case kRemoteButtonLeft_Hold:
931         case kRemoteButtonVolume_Plus_Hold:
932         case kRemoteButtonVolume_Minus_Hold:
933             /* simulate an event as long as the user holds the button */
934             b_remote_button_hold = pressedDown;
935             if( pressedDown )
936             {
937                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];
938                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
939                            withObject:buttonIdentifierNumber];
940             }
941             break;
942         case kRemoteButtonMenu:
943             [o_controls showPosition: self]; //FIXME
944             break;
945         default:
946             /* Add here whatever you want other buttons to do */
947             break;
948     }
949 }
950
951 #pragma mark -
952 #pragma mark String utility
953 // FIXME: this has nothing to do here
954
955 - (NSString *)localizedString:(const char *)psz
956 {
957     NSString * o_str = nil;
958
959     if( psz != NULL )
960     {
961         o_str = [NSString stringWithCString: psz encoding:NSUTF8StringEncoding];
962
963         if( o_str == NULL )
964         {
965             msg_Err( VLCIntf, "could not translate: %s", psz );
966             return( @"" );
967         }
968     }
969     else
970     {
971         msg_Warn( VLCIntf, "can't translate empty strings" );
972         return( @"" );
973     }
974
975     return( o_str );
976 }
977
978
979
980 - (char *)delocalizeString:(NSString *)id
981 {
982     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
983                           allowLossyConversion: NO];
984     char * psz_string;
985
986     if( o_data == nil )
987     {
988         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
989                      allowLossyConversion: YES];
990         psz_string = malloc( [o_data length] + 1 );
991         [o_data getBytes: psz_string];
992         psz_string[ [o_data length] ] = '\0';
993         msg_Err( VLCIntf, "cannot convert to the requested encoding: %s",
994                  psz_string );
995     }
996     else
997     {
998         psz_string = malloc( [o_data length] + 1 );
999         [o_data getBytes: psz_string];
1000         psz_string[ [o_data length] ] = '\0';
1001     }
1002
1003     return psz_string;
1004 }
1005
1006 /* i_width is in pixels */
1007 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
1008 {
1009     NSMutableString *o_wrapped;
1010     NSString *o_out_string;
1011     NSRange glyphRange, effectiveRange, charRange;
1012     NSRect lineFragmentRect;
1013     unsigned glyphIndex, breaksInserted = 0;
1014
1015     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
1016         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
1017         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
1018     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
1019     NSTextContainer *o_container = [[NSTextContainer alloc]
1020         initWithContainerSize: NSMakeSize(i_width, 2000)];
1021
1022     [o_layout_manager addTextContainer: o_container];
1023     [o_container release];
1024     [o_storage addLayoutManager: o_layout_manager];
1025     [o_layout_manager release];
1026
1027     o_wrapped = [o_in_string mutableCopy];
1028     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
1029
1030     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
1031             glyphIndex += effectiveRange.length) {
1032         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
1033                                             effectiveRange: &effectiveRange];
1034         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
1035                                     actualGlyphRange: &effectiveRange];
1036         if([o_wrapped lineRangeForRange:
1037                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
1038             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
1039             breaksInserted++;
1040         }
1041     }
1042     o_out_string = [NSString stringWithString: o_wrapped];
1043     [o_wrapped release];
1044     [o_storage release];
1045
1046     return o_out_string;
1047 }
1048
1049
1050 #pragma mark -
1051 #pragma mark Key Shortcuts
1052
1053 static struct
1054 {
1055     unichar i_nskey;
1056     unsigned int i_vlckey;
1057 } nskeys_to_vlckeys[] =
1058 {
1059     { NSUpArrowFunctionKey, KEY_UP },
1060     { NSDownArrowFunctionKey, KEY_DOWN },
1061     { NSLeftArrowFunctionKey, KEY_LEFT },
1062     { NSRightArrowFunctionKey, KEY_RIGHT },
1063     { NSF1FunctionKey, KEY_F1 },
1064     { NSF2FunctionKey, KEY_F2 },
1065     { NSF3FunctionKey, KEY_F3 },
1066     { NSF4FunctionKey, KEY_F4 },
1067     { NSF5FunctionKey, KEY_F5 },
1068     { NSF6FunctionKey, KEY_F6 },
1069     { NSF7FunctionKey, KEY_F7 },
1070     { NSF8FunctionKey, KEY_F8 },
1071     { NSF9FunctionKey, KEY_F9 },
1072     { NSF10FunctionKey, KEY_F10 },
1073     { NSF11FunctionKey, KEY_F11 },
1074     { NSF12FunctionKey, KEY_F12 },
1075     { NSInsertFunctionKey, KEY_INSERT },
1076     { NSHomeFunctionKey, KEY_HOME },
1077     { NSEndFunctionKey, KEY_END },
1078     { NSPageUpFunctionKey, KEY_PAGEUP },
1079     { NSPageDownFunctionKey, KEY_PAGEDOWN },
1080     { NSMenuFunctionKey, KEY_MENU },
1081     { NSTabCharacter, KEY_TAB },
1082     { NSCarriageReturnCharacter, KEY_ENTER },
1083     { NSEnterCharacter, KEY_ENTER },
1084     { NSBackspaceCharacter, KEY_BACKSPACE },
1085     {0,0}
1086 };
1087
1088 unsigned int CocoaKeyToVLC( unichar i_key )
1089 {
1090     unsigned int i;
1091
1092     for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
1093     {
1094         if( nskeys_to_vlckeys[i].i_nskey == i_key )
1095         {
1096             return nskeys_to_vlckeys[i].i_vlckey;
1097         }
1098     }
1099     return (unsigned int)i_key;
1100 }
1101
1102 - (unsigned int)VLCModifiersToCocoa:(NSString *)theString
1103 {
1104     unsigned int new = 0;
1105
1106     if([theString rangeOfString:@"Command"].location != NSNotFound)
1107         new |= NSCommandKeyMask;
1108     if([theString rangeOfString:@"Alt"].location != NSNotFound)
1109         new |= NSAlternateKeyMask;
1110     if([theString rangeOfString:@"Shift"].location != NSNotFound)
1111         new |= NSShiftKeyMask;
1112     if([theString rangeOfString:@"Ctrl"].location != NSNotFound)
1113         new |= NSControlKeyMask;
1114     return new;
1115 }
1116
1117 - (NSString *)VLCKeyToString:(NSString *)theString
1118 {
1119     if (![theString isEqualToString:@""]) {
1120         theString = [theString stringByReplacingOccurrencesOfString:@"Command" withString:@""];
1121         theString = [theString stringByReplacingOccurrencesOfString:@"Alt" withString:@""];
1122         theString = [theString stringByReplacingOccurrencesOfString:@"Shift" withString:@""];
1123         theString = [theString stringByReplacingOccurrencesOfString:@"Ctrl" withString:@""];
1124         theString = [theString stringByReplacingOccurrencesOfString:@"+" withString:@""];
1125         theString = [theString stringByReplacingOccurrencesOfString:@"-" withString:@""];
1126     }
1127     return theString;
1128 }
1129
1130
1131 /*****************************************************************************
1132  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1133  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1134  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1135  *****************************************************************************/
1136 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
1137 {
1138     unichar key = 0;
1139     vlc_value_t val;
1140     unsigned int i_pressed_modifiers = 0;
1141     const struct hotkey *p_hotkeys;
1142     int i;
1143     NSMutableString *tempString = [[[NSMutableString alloc] init] autorelease];
1144     NSMutableString *tempStringPlus = [[[NSMutableString alloc] init] autorelease];
1145
1146     val.i_int = 0;
1147     p_hotkeys = p_intf->p_libvlc->p_hotkeys;
1148
1149     i_pressed_modifiers = [o_event modifierFlags];
1150
1151     if( i_pressed_modifiers & NSShiftKeyMask ) {
1152         val.i_int |= KEY_MODIFIER_SHIFT;
1153         [tempString appendString:@"Shift-"];
1154         [tempStringPlus appendString:@"Shift+"];
1155     }
1156     if( i_pressed_modifiers & NSControlKeyMask ) {
1157         val.i_int |= KEY_MODIFIER_CTRL;
1158         [tempString appendString:@"Ctrl-"];
1159         [tempStringPlus appendString:@"Ctrl+"];
1160     }
1161     if( i_pressed_modifiers & NSAlternateKeyMask ) {
1162         val.i_int |= KEY_MODIFIER_ALT;
1163         [tempString appendString:@"Alt-"];
1164         [tempStringPlus appendString:@"Alt+"];
1165     }
1166     if( i_pressed_modifiers & NSCommandKeyMask ) {
1167         val.i_int |= KEY_MODIFIER_COMMAND;
1168         [tempString appendString:@"Command-"];
1169         [tempStringPlus appendString:@"Command+"];
1170     }
1171
1172     [tempString appendString:[[o_event charactersIgnoringModifiers] lowercaseString]];
1173     [tempStringPlus appendString:[[o_event charactersIgnoringModifiers] lowercaseString]];
1174
1175     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1176
1177     switch( key )
1178     {
1179         case NSDeleteCharacter:
1180         case NSDeleteFunctionKey:
1181         case NSDeleteCharFunctionKey:
1182         case NSBackspaceCharacter:
1183         case NSUpArrowFunctionKey:
1184         case NSDownArrowFunctionKey:
1185         case NSRightArrowFunctionKey:
1186         case NSLeftArrowFunctionKey:
1187         case NSEnterCharacter:
1188         case NSCarriageReturnCharacter:
1189             return NO;
1190     }
1191
1192     val.i_int |= CocoaKeyToVLC( key );
1193
1194     if( [o_usedHotkeys indexOfObject: tempString] != NSNotFound || [o_usedHotkeys indexOfObject: tempStringPlus] != NSNotFound )
1195     {
1196         var_SetInteger( p_intf->p_libvlc, "key-pressed", val.i_int );
1197         return YES;
1198     }
1199
1200     return NO;
1201 }
1202
1203 - (void)updateCurrentlyUsedHotkeys
1204 {
1205     NSMutableArray *o_tempArray = [[NSMutableArray alloc] init];
1206     /* Get the main Module */
1207     module_t *p_main = module_get_main();
1208     assert( p_main );
1209     unsigned confsize;
1210     module_config_t *p_config;
1211
1212     p_config = module_config_get (p_main, &confsize);
1213
1214     for (size_t i = 0; i < confsize; i++)
1215     {
1216         module_config_t *p_item = p_config + i;
1217
1218         if( CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1219            && !strncmp( p_item->psz_name , "key-", 4 )
1220            && !EMPTY_STR( p_item->psz_text ) )
1221         {
1222             if (p_item->value.psz)
1223                 [o_tempArray addObject: [NSString stringWithUTF8String:p_item->value.psz]];
1224         }
1225     }
1226     module_config_free (p_config);
1227     module_release (p_main);
1228     o_usedHotkeys = [[NSArray alloc] initWithArray: o_usedHotkeys copyItems: YES];
1229 }
1230
1231 #pragma mark -
1232 #pragma mark Interface updaters
1233 - (void)fullscreenChanged
1234 {
1235     if(! [o_mainwindow isFullscreen] )
1236         [o_mainwindow performSelectorOnMainThread:@selector(enterFullscreen) withObject:nil waitUntilDone:NO];
1237     else
1238         [o_mainwindow performSelectorOnMainThread:@selector(leaveFullscreen) withObject:nil waitUntilDone:NO];
1239 }
1240
1241 - (void)PlaylistItemChanged
1242 {
1243     input_thread_t * p_input;
1244
1245     p_input = playlist_CurrentInput( pl_Get(VLCIntf) );
1246     if( p_input && !( p_input->b_dead || !vlc_object_alive(p_input) ) )
1247     {
1248         var_AddCallback( p_input, "intf-event", InputEvent, [VLCMain sharedInstance] );
1249         [o_mainmenu setRateControlsEnabled: YES];
1250         vlc_object_release( p_input );
1251     }
1252     else
1253         [o_mainmenu setRateControlsEnabled: NO];
1254
1255     [o_playlist updateRowSelection];
1256     [o_mainwindow updateWindow];
1257     [o_mainmenu setupMenus];
1258     [o_mainmenu updatePlaybackRate];
1259 }
1260
1261 - (void)updateMainWindow
1262 {
1263     [o_mainwindow updateWindow];
1264 }
1265
1266 - (void)updateDelays
1267 {
1268     [[VLCTrackSynchronization sharedInstance] performSelectorOnMainThread: @selector(updateValues) withObject: nil waitUntilDone:NO];
1269 }
1270
1271 - (void)updateName
1272 {
1273     [o_mainwindow updateName];
1274 }
1275
1276 - (void)updatePlaybackPosition
1277 {
1278     [o_mainwindow updateTimeSlider];
1279 }
1280
1281 - (void)updateVolume
1282 {
1283     [o_mainwindow updateVolumeSlider];
1284 }
1285
1286 - (void)playlistUpdated
1287 {
1288     [self playbackStatusUpdated];
1289     [o_playlist playlistUpdated];
1290     [o_mainwindow updateWindow];
1291 }
1292
1293 - (void)updateInfoandMetaPanel
1294 {
1295     [o_playlist outlineViewSelectionDidChange:nil];
1296 }
1297
1298 - (void)playbackStatusUpdated
1299 {
1300     input_thread_t * p_input;
1301
1302     p_input = pl_CurrentInput( p_intf );
1303     if( p_input )
1304     {
1305         int state = var_GetInteger( p_input, "state" );
1306         if( state == PLAYING_S )
1307         {
1308             [[self mainMenu] setPause];
1309             [o_mainwindow setPause];
1310         }
1311         else
1312         {
1313             if (state == END_S)
1314                 [o_mainmenu setSubmenusEnabled: FALSE];
1315             [[self mainMenu] setPlay];
1316             [o_mainwindow setPlay];
1317         }
1318         vlc_object_release( p_input );
1319     }
1320 }
1321
1322 - (void)playbackModeUpdated
1323 {
1324     vlc_value_t looping,repeating;
1325     playlist_t * p_playlist = pl_Get( VLCIntf );
1326
1327     bool loop = var_GetBool( p_playlist, "loop" );
1328     bool repeat = var_GetBool( p_playlist, "repeat" );
1329     if( repeat ) {
1330         [o_mainwindow setRepeatOne];
1331         [o_mainmenu setRepeatOne];
1332     } else if( loop ) {
1333         [o_mainwindow setRepeatAll];
1334         [o_mainmenu setRepeatAll];
1335     } else {
1336         [o_mainwindow setRepeatOff];
1337         [o_mainmenu setRepeatOff];
1338     }
1339
1340     [o_mainwindow setShuffle];
1341     [o_mainmenu setShuffle];
1342 }
1343
1344 #pragma mark -
1345 #pragma mark Other objects getters
1346
1347 - (id)mainMenu
1348 {
1349     return o_mainmenu;
1350 }
1351
1352 - (id)controls
1353 {
1354     if( o_controls )
1355         return o_controls;
1356
1357     return nil;
1358 }
1359
1360 - (id)bookmarks
1361 {
1362     if (!o_bookmarks )
1363         o_bookmarks = [[VLCBookmarks alloc] init];
1364
1365     if( !nib_bookmarks_loaded )
1366         nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner: NSApp];
1367
1368     return o_bookmarks;
1369 }
1370
1371 - (id)open
1372 {
1373     if (!o_open)
1374         return nil;
1375
1376     if (!nib_open_loaded)
1377         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
1378
1379     return o_open;
1380 }
1381
1382 - (id)simplePreferences
1383 {
1384     if (!o_sprefs)
1385         o_sprefs = [[VLCSimplePrefs alloc] init];
1386
1387     if (!nib_prefs_loaded)
1388         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1389
1390     return o_sprefs;
1391 }
1392
1393 - (id)preferences
1394 {
1395     if( !o_prefs )
1396         o_prefs = [[VLCPrefs alloc] init];
1397
1398     if( !nib_prefs_loaded )
1399         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1400
1401     return o_prefs;
1402 }
1403
1404 - (id)playlist
1405 {
1406     if( o_playlist )
1407         return o_playlist;
1408
1409     return nil;
1410 }
1411
1412 - (id)info
1413 {
1414     if(! nib_info_loaded )
1415         nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: NSApp];
1416
1417     if( o_info )
1418         return o_info;
1419
1420     return nil;
1421 }
1422
1423 - (id)wizard
1424 {
1425     if( !o_wizard )
1426         o_wizard = [[VLCWizard alloc] init];
1427
1428     if( !nib_wizard_loaded )
1429     {
1430         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp];
1431         [o_wizard initStrings];
1432     }
1433     return o_wizard;
1434 }
1435
1436 - (id)getVideoViewAtPositionX: (int *)pi_x Y: (int *)pi_y withWidth: (unsigned int*)pi_width andHeight: (unsigned int*)pi_height
1437 {
1438     id videoView = [o_mainwindow videoView];
1439     NSRect videoRect = [videoView frame];
1440     int i_x = (int)videoRect.origin.x;
1441     int i_y = (int)videoRect.origin.y;
1442     unsigned int i_width = (int)videoRect.size.width;
1443     unsigned int i_height = (int)videoRect.size.height;
1444     pi_x = (int *)i_x;
1445     pi_y = (int *)i_y;
1446     pi_width = (unsigned int*)i_width;
1447     pi_height = (unsigned int*)i_height;
1448     msg_Dbg( VLCIntf, "returning videoview with x=%i, y=%i, width=%i, height=%i", i_x, i_y, i_width, i_height );
1449     return videoView;
1450 }
1451
1452 - (void)setNativeVideoSize:(NSSize)size
1453 {
1454     [o_mainwindow setNativeVideoSize:size];
1455 }
1456
1457 - (id)embeddedList
1458 {
1459     if( o_embedded_list )
1460         return o_embedded_list;
1461
1462     return nil;
1463 }
1464
1465 - (id)coreDialogProvider
1466 {
1467     if( o_coredialogs )
1468         return o_coredialogs;
1469
1470     return nil;
1471 }
1472
1473 - (id)eyeTVController
1474 {
1475     if( o_eyetv )
1476         return o_eyetv;
1477
1478     return nil;
1479 }
1480
1481 - (id)appleRemoteController
1482 {
1483         return o_remote;
1484 }
1485
1486 - (void)setActiveVideoPlayback:(BOOL)b_value
1487 {
1488     b_active_videoplayback = b_value;
1489     [o_mainwindow setVideoplayEnabled];
1490     [o_mainwindow togglePlaylist:nil];
1491 }
1492
1493 - (BOOL)activeVideoPlayback
1494 {
1495     return b_active_videoplayback;
1496 }
1497
1498 #pragma mark -
1499 #pragma mark Crash Log
1500 - (void)sendCrashLog:(NSString *)crashLog withUserComment:(NSString *)userComment
1501 {
1502     NSString *urlStr = @"http://jones.videolan.org/crashlog/sendcrashreport.php";
1503     NSURL *url = [NSURL URLWithString:urlStr];
1504
1505     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
1506     [req setHTTPMethod:@"POST"];
1507
1508     NSString * email;
1509     if( [o_crashrep_includeEmail_ckb state] == NSOnState )
1510     {
1511         ABPerson * contact = [[ABAddressBook sharedAddressBook] me];
1512         ABMultiValue *emails = [contact valueForProperty:kABEmailProperty];
1513         email = [emails valueAtIndex:[emails indexForIdentifier:
1514                     [emails primaryIdentifier]]];
1515     }
1516     else
1517         email = [NSString string];
1518
1519     NSString *postBody;
1520     postBody = [NSString stringWithFormat:@"CrashLog=%@&Comment=%@&Email=%@\r\n",
1521             [crashLog stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
1522             [userComment stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
1523             [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1524
1525     [req setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
1526
1527     /* Released from delegate */
1528     crashLogURLConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
1529 }
1530
1531 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
1532 {
1533     [crashLogURLConnection release];
1534     crashLogURLConnection = nil;
1535 }
1536
1537 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
1538 {
1539     NSRunCriticalAlertPanel(_NS("Error when sending the Crash Report"), [error localizedDescription], @"OK", nil, nil);
1540     [crashLogURLConnection release];
1541     crashLogURLConnection = nil;
1542 }
1543
1544 - (NSString *)latestCrashLogPathPreviouslySeen:(BOOL)previouslySeen
1545 {
1546     NSString * crashReporter = [@"~/Library/Logs/CrashReporter" stringByExpandingTildeInPath];
1547     NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:crashReporter];
1548     NSString *fname;
1549     NSString * latestLog = nil;
1550     int year  = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportYear"] : 0;
1551     int month = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportMonth"]: 0;
1552     int day   = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportDay"]  : 0;
1553     int hours = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportHours"]: 0;
1554
1555     while (fname = [direnum nextObject])
1556     {
1557         [direnum skipDescendents];
1558         if([fname hasPrefix:@"VLC"] && [fname hasSuffix:@"crash"])
1559         {
1560             NSArray * compo = [fname componentsSeparatedByString:@"_"];
1561             if( [compo count] < 3 ) continue;
1562             compo = [[compo objectAtIndex:1] componentsSeparatedByString:@"-"];
1563             if( [compo count] < 4 ) continue;
1564
1565             // Dooh. ugly.
1566             if( year < [[compo objectAtIndex:0] intValue] ||
1567                 (year ==[[compo objectAtIndex:0] intValue] &&
1568                  (month < [[compo objectAtIndex:1] intValue] ||
1569                   (month ==[[compo objectAtIndex:1] intValue] &&
1570                    (day   < [[compo objectAtIndex:2] intValue] ||
1571                     (day   ==[[compo objectAtIndex:2] intValue] &&
1572                       hours < [[compo objectAtIndex:3] intValue] ))))))
1573             {
1574                 year  = [[compo objectAtIndex:0] intValue];
1575                 month = [[compo objectAtIndex:1] intValue];
1576                 day   = [[compo objectAtIndex:2] intValue];
1577                 hours = [[compo objectAtIndex:3] intValue];
1578                 latestLog = [crashReporter stringByAppendingPathComponent:fname];
1579             }
1580         }
1581     }
1582
1583     if(!(latestLog && [[NSFileManager defaultManager] fileExistsAtPath:latestLog]))
1584         return nil;
1585
1586     if( !previouslySeen )
1587     {
1588         [[NSUserDefaults standardUserDefaults] setInteger:year  forKey:@"LatestCrashReportYear"];
1589         [[NSUserDefaults standardUserDefaults] setInteger:month forKey:@"LatestCrashReportMonth"];
1590         [[NSUserDefaults standardUserDefaults] setInteger:day   forKey:@"LatestCrashReportDay"];
1591         [[NSUserDefaults standardUserDefaults] setInteger:hours forKey:@"LatestCrashReportHours"];
1592     }
1593     return latestLog;
1594 }
1595
1596 - (NSString *)latestCrashLogPath
1597 {
1598     return [self latestCrashLogPathPreviouslySeen:YES];
1599 }
1600
1601 - (void)lookForCrashLog
1602 {
1603     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1604     // This pref key doesn't exists? this VLC is an upgrade, and this crash log come from previous version
1605     BOOL areCrashLogsTooOld = ![[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportYear"];
1606     NSString * latestLog = [self latestCrashLogPathPreviouslySeen:NO];
1607     if( latestLog && !areCrashLogsTooOld )
1608         [NSApp runModalForWindow: o_crashrep_win];
1609     [o_pool release];
1610 }
1611
1612 - (IBAction)crashReporterAction:(id)sender
1613 {
1614     if( sender == o_crashrep_send_btn )
1615         [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]];
1616
1617     [NSApp stopModal];
1618     [o_crashrep_win orderOut: sender];
1619 }
1620
1621 - (IBAction)openCrashLog:(id)sender
1622 {
1623     NSString * latestLog = [self latestCrashLogPath];
1624     if( latestLog )
1625     {
1626         [[NSWorkspace sharedWorkspace] openFile: latestLog withApplication: @"Console"];
1627     }
1628     else
1629     {
1630         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.") );
1631     }
1632 }
1633
1634 #pragma mark -
1635 #pragma mark Remove old prefs
1636
1637 - (void)_removeOldPreferences
1638 {
1639     static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion";
1640     static const int kCurrentPreferencesVersion = 1;
1641     int version = [[NSUserDefaults standardUserDefaults] integerForKey:kVLCPreferencesVersion];
1642     if( version >= kCurrentPreferencesVersion ) return;
1643
1644     NSArray *libraries = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
1645         NSUserDomainMask, YES);
1646     if( !libraries || [libraries count] == 0) return;
1647     NSString * preferences = [[libraries objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
1648
1649     /* File not found, don't attempt anything */
1650     if(![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"VLC"]] &&
1651        ![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc.plist"]] )
1652     {
1653         [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1654         return;
1655     }
1656
1657     int res = NSRunInformationalAlertPanel(_NS("Remove old preferences?"),
1658                 _NS("We just found an older version of VLC's preferences files."),
1659                 _NS("Move To Trash and Relaunch VLC"), _NS("Ignore"), nil, nil);
1660     if( res != NSOKButton )
1661     {
1662         [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1663         return;
1664     }
1665
1666     NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc.plist", @"VLC", nil];
1667
1668     /* Move the file to trash so that user can find them later */
1669     [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:nil files:ourPreferences tag:0];
1670
1671     /* really reset the defaults from now on */
1672     [NSUserDefaults resetStandardUserDefaults];
1673
1674     [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1675     [[NSUserDefaults standardUserDefaults] synchronize];
1676
1677     /* Relaunch now */
1678     const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];
1679
1680     /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
1681     if(fork() != 0)
1682     {
1683         exit(0);
1684         return;
1685     }
1686     execl(path, path, NULL);
1687 }
1688
1689 #pragma mark -
1690 #pragma mark Errors, warnings and messages
1691 - (IBAction)showMessagesPanel:(id)sender
1692 {
1693     [o_msgs_panel makeKeyAndOrderFront: sender];
1694 }
1695
1696 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1697 {
1698     if( [o_notification object] == o_msgs_panel )
1699         [self updateMessageDisplay];
1700 }
1701
1702 - (void)updateMessageDisplay
1703 {
1704     if( [o_msgs_panel isVisible] && b_msg_arr_changed )
1705     {
1706         id o_msg;
1707         NSEnumerator * o_enum;
1708
1709         [o_messages setString: @""];
1710
1711         [o_msg_lock lock];
1712
1713         o_enum = [o_msg_arr objectEnumerator];
1714
1715         while( ( o_msg = [o_enum nextObject] ) != nil )
1716         {
1717             [o_messages insertText: o_msg];
1718         }
1719
1720         b_msg_arr_changed = NO;
1721         [o_msg_lock unlock];
1722     }
1723 }
1724
1725 - (void)processReceivedlibvlcMessage:(const msg_item_t *)item
1726 {
1727     NSColor *o_white = [NSColor whiteColor];
1728     NSColor *o_red = [NSColor redColor];
1729     NSColor *o_yellow = [NSColor yellowColor];
1730     NSColor *o_gray = [NSColor grayColor];
1731
1732     NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1733     static const char * ppsz_type[4] = { ": ", " error: ", " warning: ", " debug: " };
1734
1735     NSDictionary *o_attr;
1736     NSAttributedString *o_msg_color;
1737
1738     int i_type = item->i_type;
1739
1740     [o_msg_lock lock];
1741
1742     if( [o_msg_arr count] + 2 > 600 )
1743     {
1744         [o_msg_arr removeObjectAtIndex: 0];
1745         [o_msg_arr removeObjectAtIndex: 1];
1746     }
1747
1748     o_attr = [NSDictionary dictionaryWithObject: o_gray forKey: NSForegroundColorAttributeName];
1749     o_msg_color = [[NSAttributedString alloc] initWithString: [NSString stringWithFormat: @"%s%s", item->psz_module, ppsz_type[i_type]] attributes: o_attr];
1750     [o_msg_arr addObject: [o_msg_color autorelease]];
1751
1752     o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type] forKey: NSForegroundColorAttributeName];
1753     o_msg_color = [[NSAttributedString alloc] initWithString: [NSString stringWithFormat: @"%s\n", item->psz_msg] attributes: o_attr];
1754     [o_msg_arr addObject: [o_msg_color autorelease]];
1755
1756     b_msg_arr_changed = YES;
1757     [o_msg_lock unlock];
1758
1759     [self updateMessageDisplay];
1760 }
1761
1762 - (IBAction)saveDebugLog:(id)sender
1763 {
1764     NSSavePanel * saveFolderPanel = [[NSSavePanel alloc] init];
1765
1766     [saveFolderPanel setCanSelectHiddenExtension: NO];
1767     [saveFolderPanel setCanCreateDirectories: YES];
1768     [saveFolderPanel setAllowedFileTypes: [NSArray arrayWithObject:@"rtfd"]];
1769     [saveFolderPanel beginSheetForDirectory:nil file: [NSString stringWithFormat: _NS("VLC Debug Log (%s).rtfd"), VERSION_MESSAGE] modalForWindow: o_msgs_panel modalDelegate:self didEndSelector:@selector(saveDebugLogAsRTF:returnCode:contextInfo:) contextInfo:nil];
1770 }
1771
1772 - (void)saveDebugLogAsRTF: (NSSavePanel *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
1773 {
1774     BOOL b_returned;
1775     if( returnCode == NSOKButton )
1776     {
1777         b_returned = [o_messages writeRTFDToFile: [[sheet URL] path] atomically: YES];
1778         if(! b_returned )
1779             msg_Warn( p_intf, "Error while saving the debug log" );
1780     }
1781 }
1782
1783 #pragma mark -
1784 #pragma mark Playlist toggling
1785
1786 - (void)updateTogglePlaylistState
1787 {
1788     [[self playlist] outlineViewSelectionDidChange: NULL];
1789 }
1790
1791 #pragma mark -
1792
1793 @end
1794
1795 @implementation VLCMain (Internal)
1796
1797 - (void)handlePortMessage:(NSPortMessage *)o_msg
1798 {
1799     id ** val;
1800     NSData * o_data;
1801     NSValue * o_value;
1802     NSInvocation * o_inv;
1803     NSConditionLock * o_lock;
1804
1805     o_data = [[o_msg components] lastObject];
1806     o_inv = *((NSInvocation **)[o_data bytes]);
1807     [o_inv getArgument: &o_value atIndex: 2];
1808     val = (id **)[o_value pointerValue];
1809     [o_inv setArgument: val[1] atIndex: 2];
1810     o_lock = *(val[0]);
1811
1812     [o_lock lock];
1813     [o_inv invoke];
1814     [o_lock unlockWithCondition: 1];
1815 }
1816 - (void)resetMediaKeyJump
1817 {
1818     b_mediakeyJustJumped = NO;
1819 }
1820 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
1821 {
1822     b_mediaKeySupport = config_GetInt( VLCIntf, "macosx-mediakeys" );
1823     if (b_mediaKeySupport) {
1824         if (!o_mediaKeyController)
1825             o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
1826         [o_mediaKeyController startWatchingMediaKeys];
1827     }
1828     else if (!b_mediaKeySupport && o_mediaKeyController)
1829     {
1830         int returnedValue = NSRunInformationalAlertPanel(_NS("Relaunch required"),
1831                                                _NS("To make sure that VLC no longer listens to your media key events, it needs to be restarted."),
1832                                                _NS("Relaunch VLC"), _NS("Ignore"), nil, nil);
1833         if( returnedValue == NSOKButton )
1834         {
1835             /* Relaunch now */
1836             const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];
1837
1838             /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
1839             if(fork() != 0)
1840             {
1841                 exit(0);
1842                 return;
1843             }
1844             execl(path, path, NULL);
1845         }
1846     }
1847 }
1848
1849 @end
1850
1851 /*****************************************************************************
1852  * VLCApplication interface
1853  *****************************************************************************/
1854
1855 @implementation VLCApplication
1856 // when user selects the quit menu from dock it sends a terminate:
1857 // but we need to send a stop: to properly exits libvlc.
1858 // However, we are not able to change the action-method sent by this standard menu item.
1859 // thus we override terminat: to send a stop:
1860 // see [af97f24d528acab89969d6541d83f17ce1ecd580] that introduced the removal of setjmp() and longjmp()
1861 - (void)terminate:(id)sender
1862 {
1863     [self activateIgnoringOtherApps:YES];
1864     [self stop:sender];
1865 }
1866
1867 @end