39ffeb5ea726b368c254f540f1e1bb52876d012e
[vlc.git] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Derk-Jan Hartman <hartman at videolan.org>
9  *          Felix Paul K├╝hne <fkuehne at videolan dot org>
10  *          Pierre d'Herbemont <pdherbemont # videolan org>
11  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <stdlib.h>                                      /* malloc(), free() */
36 #include <string.h>
37 #include <vlc_common.h>
38 #include <vlc_keys.h>
39 #include <vlc_dialog.h>
40 #include <vlc_url.h>
41 #include <vlc_modules.h>
42 #include <vlc_plugin.h>
43 #include <vlc_vout_display.h>
44 #include <unistd.h> /* execl() */
45
46 #import "CompatibilityFixes.h"
47 #import "intf.h"
48 #import "MainMenu.h"
49 #import "VideoView.h"
50 #import "prefs.h"
51 #import "playlist.h"
52 #import "playlistinfo.h"
53 #import "controls.h"
54 #import "open.h"
55 #import "wizard.h"
56 #import "bookmarks.h"
57 #import "coredialogs.h"
58 #import "AppleRemote.h"
59 #import "eyetv.h"
60 #import "simple_prefs.h"
61 #import "CoreInteraction.h"
62 #import "TrackSynchronization.h"
63 #import "ExtensionsManager.h"
64 #import "BWQuincyManager.h"
65 #import "ControlsBar.h"
66
67 #import "VideoEffects.h"
68 #import "AudioEffects.h"
69
70 #ifdef HAVE_SPARKLE
71 #import <Sparkle/Sparkle.h>                 /* we're the update delegate */
72 #endif
73
74 #import "iTunes.h"
75 #import "Spotify.h"
76
77 /*****************************************************************************
78  * Local prototypes.
79  *****************************************************************************/
80 static void Run (intf_thread_t *p_intf);
81
82 static void updateProgressPanel (void *, const char *, float);
83 static bool checkProgressPanel (void *);
84 static void destroyProgressPanel (void *);
85
86 static int InputEvent(vlc_object_t *, const char *,
87                       vlc_value_t, vlc_value_t, void *);
88 static int PLItemChanged(vlc_object_t *, const char *,
89                          vlc_value_t, vlc_value_t, void *);
90 static int PLItemUpdated(vlc_object_t *, const char *,
91                          vlc_value_t, vlc_value_t, void *);
92
93 static int PlaybackModeUpdated(vlc_object_t *, const char *,
94                                vlc_value_t, vlc_value_t, void *);
95 static int VolumeUpdated(vlc_object_t *, const char *,
96                          vlc_value_t, vlc_value_t, void *);
97 static int BossCallback(vlc_object_t *, const char *,
98                          vlc_value_t, vlc_value_t, void *);
99
100 #pragma mark -
101 #pragma mark VLC Interface Object Callbacks
102
103 static bool b_intf_starting = false;
104 static vlc_mutex_t start_mutex = VLC_STATIC_MUTEX;
105 static vlc_cond_t  start_cond = VLC_STATIC_COND;
106
107 /*****************************************************************************
108  * OpenIntf: initialize interface
109  *****************************************************************************/
110 int OpenIntf (vlc_object_t *p_this)
111 {
112     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
113     [VLCApplication sharedApplication];
114
115     intf_thread_t *p_intf = (intf_thread_t*) p_this;
116     msg_Dbg(p_intf, "Starting macosx interface");
117     Run(p_intf);
118
119     [o_pool release];
120     return VLC_SUCCESS;
121 }
122
123 static NSLock * o_vout_provider_lock = nil;
124
125 static int WindowControl(vout_window_t *, int i_query, va_list);
126
127 int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg)
128 {
129     if (cfg->type != VOUT_WINDOW_TYPE_INVALID
130      && cfg->type != VOUT_WINDOW_TYPE_NSOBJECT)
131         return VLC_EGENERIC;
132
133     msg_Dbg(p_wnd, "Opening video window");
134
135     /*
136      * HACK: Wait 200ms for the interface to come up.
137      * WindowOpen might be called before the mac intf is started. Lets wait until OpenIntf gets called
138      * and does basic initialization. Enqueuing the vout controller request into the main loop later on
139      * ensures that the actual window is created after the interface is fully initialized
140      * (applicationDidFinishLaunching).
141      *
142      * Timeout is needed as the mac intf is not always started at all.
143      */
144     mtime_t deadline = mdate() + 200000;
145     vlc_mutex_lock(&start_mutex);
146     while (!b_intf_starting) {
147         if (vlc_cond_timedwait(&start_cond, &start_mutex, deadline)) {
148             break; // timeout
149         }
150     }
151
152     if (!b_intf_starting) {
153         msg_Err(p_wnd, "Cannot create vout as Mac OS X interface was not found");
154         vlc_mutex_unlock(&start_mutex);
155         return VLC_EGENERIC;
156     }
157     vlc_mutex_unlock(&start_mutex);
158
159     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
160
161     NSRect proposedVideoViewPosition = NSMakeRect(cfg->x, cfg->y, cfg->width, cfg->height);
162
163     [o_vout_provider_lock lock];
164     VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
165     if (!o_vout_controller) {
166         [o_vout_provider_lock unlock];
167         [o_pool release];
168         return VLC_EGENERIC;
169     }
170
171     SEL sel = @selector(setupVoutForWindow:withProposedVideoViewPosition:);
172     NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
173     [inv setTarget:o_vout_controller];
174     [inv setSelector:sel];
175     [inv setArgument:&p_wnd atIndex:2]; // starting at 2!
176     [inv setArgument:&proposedVideoViewPosition atIndex:3];
177
178     [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
179                        waitUntilDone:YES];
180
181     VLCVoutView *videoView = nil;
182     [inv getReturnValue:&videoView];
183
184     // this method is not supposed to fail
185     assert(videoView != nil);
186
187     msg_Dbg(VLCIntf, "returning videoview with proposed position x=%i, y=%i, width=%i, height=%i", cfg->x, cfg->y, cfg->width, cfg->height);
188     p_wnd->handle.nsobject = videoView;
189
190     [o_vout_provider_lock unlock];
191
192     p_wnd->type = VOUT_WINDOW_TYPE_NSOBJECT;
193     p_wnd->control = WindowControl;
194
195     [o_pool release];
196     return VLC_SUCCESS;
197 }
198
199 static int WindowControl(vout_window_t *p_wnd, int i_query, va_list args)
200 {
201     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
202
203     [o_vout_provider_lock lock];
204     VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
205     if (!o_vout_controller) {
206         [o_vout_provider_lock unlock];
207         [o_pool release];
208         return VLC_EGENERIC;
209     }
210
211     switch(i_query) {
212         case VOUT_WINDOW_SET_STATE:
213         {
214             unsigned i_state = va_arg(args, unsigned);
215
216             if (i_state & VOUT_WINDOW_STATE_BELOW)
217             {
218                 msg_Dbg(p_wnd, "Ignore change to VOUT_WINDOW_STATE_BELOW");
219                 goto out;
220             }
221
222             NSInteger i_cooca_level = NSNormalWindowLevel;
223             if (i_state & VOUT_WINDOW_STATE_ABOVE)
224                 i_cooca_level = NSStatusWindowLevel;
225
226             SEL sel = @selector(setWindowLevel:forWindow:);
227             NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
228             [inv setTarget:o_vout_controller];
229             [inv setSelector:sel];
230             [inv setArgument:&i_cooca_level atIndex:2]; // starting at 2!
231             [inv setArgument:&p_wnd atIndex:3];
232             [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
233                                waitUntilDone:NO];
234
235             break;
236         }
237         case VOUT_WINDOW_SET_SIZE:
238         {
239             unsigned int i_width  = va_arg(args, unsigned int);
240             unsigned int i_height = va_arg(args, unsigned int);
241
242             NSSize newSize = NSMakeSize(i_width, i_height);
243             SEL sel = @selector(setNativeVideoSize:forWindow:);
244             NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
245             [inv setTarget:o_vout_controller];
246             [inv setSelector:sel];
247             [inv setArgument:&newSize atIndex:2]; // starting at 2!
248             [inv setArgument:&p_wnd atIndex:3];
249             [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
250                                waitUntilDone:NO];
251
252             break;
253         }
254         case VOUT_WINDOW_SET_FULLSCREEN:
255         {
256             if (var_InheritBool(VLCIntf, "video-wallpaper")) {
257                 msg_Dbg(p_wnd, "Ignore fullscreen event as video-wallpaper is on");
258                 goto out;
259             }
260
261             int i_full = va_arg(args, int);
262             BOOL b_animation = YES;
263
264             SEL sel = @selector(setFullscreen:forWindow:withAnimation:);
265             NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
266             [inv setTarget:o_vout_controller];
267             [inv setSelector:sel];
268             [inv setArgument:&i_full atIndex:2]; // starting at 2!
269             [inv setArgument:&p_wnd atIndex:3];
270             [inv setArgument:&b_animation atIndex:4];
271             [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
272                                waitUntilDone:NO];
273
274             break;
275         }
276         default:
277         {
278             msg_Warn(p_wnd, "unsupported control query");
279             [o_vout_provider_lock unlock];
280             [o_pool release];
281             return VLC_EGENERIC;
282         }
283     }
284
285 out:
286     [o_vout_provider_lock unlock];
287     [o_pool release];
288     return VLC_SUCCESS;
289 }
290
291 void WindowClose(vout_window_t *p_wnd)
292 {
293     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
294
295     [o_vout_provider_lock lock];
296     VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
297     if (!o_vout_controller) {
298         [o_vout_provider_lock unlock];
299         [o_pool release];
300         return;
301     }
302
303     [o_vout_controller performSelectorOnMainThread:@selector(removeVoutforDisplay:) withObject:[NSValue valueWithPointer:p_wnd] waitUntilDone:NO];
304     [o_vout_provider_lock unlock];
305
306     [o_pool release];
307 }
308
309 /* Used to abort the app.exec() on OSX after libvlc_Quit is called */
310 #include "../../../lib/libvlc_internal.h" /* libvlc_SetExitHandler */
311
312 static void QuitVLC( void *obj )
313 {
314     [[VLCApplication sharedApplication] performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
315 }
316
317 /*****************************************************************************
318  * Run: main loop
319  *****************************************************************************/
320 static NSLock * o_appLock = nil;    // controls access to f_appExit
321
322 static void Run(intf_thread_t *p_intf)
323 {
324     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
325     [VLCApplication sharedApplication];
326
327     o_appLock = [[NSLock alloc] init];
328     o_vout_provider_lock = [[NSLock alloc] init];
329
330     libvlc_SetExitHandler(p_intf->p_libvlc, QuitVLC, p_intf);
331     [[VLCMain sharedInstance] setIntf: p_intf];
332
333     vlc_mutex_lock(&start_mutex);
334     b_intf_starting = true;
335     vlc_cond_signal(&start_cond);
336     vlc_mutex_unlock(&start_mutex);
337
338     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
339
340     [NSApp run];
341     msg_Dbg(p_intf, "Run loop has been stopped");
342     [[VLCMain sharedInstance] applicationWillTerminate:nil];
343     [o_appLock release];
344     [o_vout_provider_lock release];
345     o_vout_provider_lock = nil;
346     [o_pool release];
347
348     raise(SIGTERM);
349 }
350
351 #pragma mark -
352 #pragma mark Variables Callback
353
354 static int InputEvent(vlc_object_t *p_this, const char *psz_var,
355                        vlc_value_t oldval, vlc_value_t new_val, void *param)
356 {
357     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
358     switch (new_val.i_int) {
359         case INPUT_EVENT_STATE:
360             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
361             break;
362         case INPUT_EVENT_RATE:
363             [[[VLCMain sharedInstance] mainMenu] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
364             break;
365         case INPUT_EVENT_POSITION:
366             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject: nil waitUntilDone:NO];
367             break;
368         case INPUT_EVENT_TITLE:
369         case INPUT_EVENT_CHAPTER:
370             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
371             break;
372         case INPUT_EVENT_CACHE:
373             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
374             break;
375         case INPUT_EVENT_STATISTICS:
376             dispatch_async(dispatch_get_main_queue(), ^{
377                 [[[VLCMain sharedInstance] info] updateStatistics];
378             });
379             break;
380         case INPUT_EVENT_ES:
381             break;
382         case INPUT_EVENT_TELETEXT:
383             break;
384         case INPUT_EVENT_AOUT:
385             break;
386         case INPUT_EVENT_VOUT:
387             break;
388         case INPUT_EVENT_ITEM_META:
389         case INPUT_EVENT_ITEM_INFO:
390             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
391             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
392             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO];
393             break;
394         case INPUT_EVENT_BOOKMARK:
395             break;
396         case INPUT_EVENT_RECORD:
397             [[VLCMain sharedInstance] updateRecordState: var_GetBool(p_this, "record")];
398             break;
399         case INPUT_EVENT_PROGRAM:
400             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
401             break;
402         case INPUT_EVENT_ITEM_EPG:
403             break;
404         case INPUT_EVENT_SIGNAL:
405             break;
406
407         case INPUT_EVENT_ITEM_NAME:
408             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
409             break;
410
411         case INPUT_EVENT_AUDIO_DELAY:
412         case INPUT_EVENT_SUBTITLE_DELAY:
413             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
414             break;
415
416         case INPUT_EVENT_DEAD:
417             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
418             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO];
419             break;
420
421         default:
422             break;
423     }
424
425     [o_pool release];
426     return VLC_SUCCESS;
427 }
428
429 static int PLItemChanged(vlc_object_t *p_this, const char *psz_var,
430                          vlc_value_t oldval, vlc_value_t new_val, void *param)
431 {
432     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
433
434     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO];
435
436     [o_pool release];
437     return VLC_SUCCESS;
438 }
439
440 /**
441  * Callback for item-change variable. Is triggered after update of duration or metadata.
442  */
443 static int PLItemUpdated(vlc_object_t *p_this, const char *psz_var,
444                          vlc_value_t oldval, vlc_value_t new_val, void *param)
445 {
446     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
447
448     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemUpdated) withObject:nil waitUntilDone:NO];
449
450     [o_pool release];
451     return VLC_SUCCESS;
452 }
453
454 static int PLItemAppended(vlc_object_t *p_this, const char *psz_var,
455                            vlc_value_t oldval, vlc_value_t new_val, void *param)
456 {
457     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
458
459     playlist_add_t *p_add = new_val.p_address;
460     NSArray *o_val = [NSArray arrayWithObjects:[NSNumber numberWithInt:p_add->i_node], [NSNumber numberWithInt:p_add->i_item], nil];
461     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemAppended:) withObject:o_val waitUntilDone:NO];
462
463     [o_pool release];
464     return VLC_SUCCESS;
465 }
466
467 static int PLItemRemoved(vlc_object_t *p_this, const char *psz_var,
468                            vlc_value_t oldval, vlc_value_t new_val, void *param)
469 {
470     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
471
472     NSNumber *o_val = [NSNumber numberWithInt:new_val.i_int];
473     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemRemoved:) withObject:o_val waitUntilDone:NO];
474
475     [o_pool release];
476     return VLC_SUCCESS;
477 }
478
479 static int PlaybackModeUpdated(vlc_object_t *p_this, const char *psz_var,
480                          vlc_value_t oldval, vlc_value_t new_val, void *param)
481 {
482     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
483     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackModeUpdated) withObject:nil waitUntilDone:NO];
484
485     [o_pool release];
486     return VLC_SUCCESS;
487 }
488
489 static int VolumeUpdated(vlc_object_t *p_this, const char *psz_var,
490                          vlc_value_t oldval, vlc_value_t new_val, void *param)
491 {
492     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
493     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateVolume) withObject:nil waitUntilDone:NO];
494
495     [o_pool release];
496     return VLC_SUCCESS;
497 }
498
499 static int BossCallback(vlc_object_t *p_this, const char *psz_var,
500                         vlc_value_t oldval, vlc_value_t new_val, void *param)
501 {
502     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
503
504     [[VLCCoreInteraction sharedInstance] performSelectorOnMainThread:@selector(pause) withObject:nil waitUntilDone:NO];
505     [[VLCApplication sharedApplication] hide:nil];
506
507     [o_pool release];
508     return VLC_SUCCESS;
509 }
510
511 /*****************************************************************************
512  * ShowController: Callback triggered by the show-intf playlist variable
513  * through the ShowIntf-control-intf, to let us show the controller-win;
514  * usually when in fullscreen-mode
515  *****************************************************************************/
516 static int ShowController(vlc_object_t *p_this, const char *psz_variable,
517                      vlc_value_t old_val, vlc_value_t new_val, void *param)
518 {
519     intf_thread_t * p_intf = VLCIntf;
520     if (p_intf) {
521         playlist_t * p_playlist = pl_Get(p_intf);
522         BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen");
523         if (b_fullscreen)
524             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
525         else if (!strcmp(psz_variable, "intf-show"))
526             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showMainWindow) withObject:nil waitUntilDone:NO];
527     }
528
529     return VLC_SUCCESS;
530 }
531
532 /*****************************************************************************
533  * DialogCallback: Callback triggered by the "dialog-*" variables
534  * to let the intf display error and interaction dialogs
535  *****************************************************************************/
536 static int DialogCallback(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
537 {
538     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
539
540     if ([[NSString stringWithUTF8String:type] isEqualToString: @"dialog-progress-bar"]) {
541         /* the progress panel needs to update itself and therefore wants special treatment within this context */
542         dialog_progress_bar_t *p_dialog = (dialog_progress_bar_t *)value.p_address;
543
544         p_dialog->pf_update = updateProgressPanel;
545         p_dialog->pf_check = checkProgressPanel;
546         p_dialog->pf_destroy = destroyProgressPanel;
547         p_dialog->p_sys = VLCIntf->p_libvlc;
548     }
549
550     NSValue *o_value = [NSValue valueWithPointer:value.p_address];
551     [[[VLCMain sharedInstance] coreDialogProvider] performEventWithObject: o_value ofType: type];
552
553     [o_pool release];
554     return VLC_SUCCESS;
555 }
556
557 void updateProgressPanel (void *priv, const char *text, float value)
558 {
559     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
560
561     NSString *o_txt = toNSStr(text);
562     dispatch_async(dispatch_get_main_queue(), ^{
563         [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)];
564     });
565
566     [o_pool release];
567 }
568
569 void destroyProgressPanel (void *priv)
570 {
571     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
572
573     if ([[NSApplication sharedApplication] isRunning])
574         [[[VLCMain sharedInstance] coreDialogProvider] performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];
575
576     [o_pool release];
577 }
578
579 bool checkProgressPanel (void *priv)
580 {
581     return [[[VLCMain sharedInstance] coreDialogProvider] progressCancelled];
582 }
583
584 #pragma mark -
585 #pragma mark Helpers
586
587 input_thread_t *getInput(void)
588 {
589     intf_thread_t *p_intf = VLCIntf;
590     if (!p_intf)
591         return NULL;
592     return pl_CurrentInput(p_intf);
593 }
594
595 vout_thread_t *getVout(void)
596 {
597     input_thread_t *p_input = getInput();
598     if (!p_input)
599         return NULL;
600     vout_thread_t *p_vout = input_GetVout(p_input);
601     vlc_object_release(p_input);
602     return p_vout;
603 }
604
605 vout_thread_t *getVoutForActiveWindow(void)
606 {
607     vout_thread_t *p_vout = nil;
608
609     id currentWindow = [NSApp keyWindow];
610     if ([currentWindow respondsToSelector:@selector(videoView)]) {
611         VLCVoutView *videoView = [currentWindow videoView];
612         if (videoView) {
613             p_vout = [videoView voutThread];
614         }
615     }
616
617     if (!p_vout)
618         p_vout = getVout();
619
620     return p_vout;
621 }
622
623 audio_output_t *getAout(void)
624 {
625     intf_thread_t *p_intf = VLCIntf;
626     if (!p_intf)
627         return NULL;
628     return playlist_GetAout(pl_Get(p_intf));
629 }
630
631 #pragma mark -
632 #pragma mark Private
633
634 @interface VLCMain () <BWQuincyManagerDelegate>
635 - (void)removeOldPreferences;
636 @end
637
638 @interface VLCMain (Internal)
639 - (void)resetMediaKeyJump;
640 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification;
641 @end
642
643 /*****************************************************************************
644  * VLCMain implementation
645  *****************************************************************************/
646 @implementation VLCMain
647
648 @synthesize voutController=o_vout_controller;
649 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
650
651 #pragma mark -
652 #pragma mark Initialization
653
654 static VLCMain *_o_sharedMainInstance = nil;
655
656 + (VLCMain *)sharedInstance
657 {
658     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
659 }
660
661 - (id)init
662 {
663     if (_o_sharedMainInstance) {
664         [self dealloc];
665         return _o_sharedMainInstance;
666     } else
667         _o_sharedMainInstance = [super init];
668
669     p_intf = NULL;
670     p_current_input = NULL;
671
672     o_open = [[VLCOpen alloc] init];
673     o_coredialogs = [[VLCCoreDialogProvider alloc] init];
674     o_mainmenu = [[VLCMainMenu alloc] init];
675     o_coreinteraction = [[VLCCoreInteraction alloc] init];
676     o_eyetv = [[VLCEyeTVController alloc] init];
677
678     /* announce our launch to a potential eyetv plugin */
679     [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
680                                                                    object: @"VLCEyeTVSupport"
681                                                                  userInfo: NULL
682                                                        deliverImmediately: YES];
683
684     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
685     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"LiveUpdateTheMessagesPanel"];
686     [defaults registerDefaults:appDefaults];
687
688     o_vout_controller = [[VLCVoutWindowController alloc] init];
689
690     informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);
691
692     return _o_sharedMainInstance;
693 }
694
695 - (void)setIntf: (intf_thread_t *)p_mainintf
696 {
697     p_intf = p_mainintf;
698 }
699
700 - (intf_thread_t *)intf
701 {
702     return p_intf;
703 }
704
705 - (void)awakeFromNib
706 {
707     playlist_t *p_playlist;
708     if (!p_intf) return;
709     var_Create(p_intf, "intf-change", VLC_VAR_BOOL);
710
711     /* Check if we already did this once. Opening the other nibs calls it too,
712      because VLCMain is the owner */
713     if (nib_main_loaded)
714         return;
715
716     p_playlist = pl_Get(p_intf);
717
718     var_AddCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
719     var_AddCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
720     var_AddCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self);
721     var_AddCallback(p_playlist, "item-change", PLItemUpdated, self);
722     var_AddCallback(p_playlist, "input-current", PLItemChanged, self);
723     var_AddCallback(p_playlist, "playlist-item-append", PLItemAppended, self);
724     var_AddCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self);
725     var_AddCallback(p_playlist, "random", PlaybackModeUpdated, self);
726     var_AddCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
727     var_AddCallback(p_playlist, "loop", PlaybackModeUpdated, self);
728     var_AddCallback(p_playlist, "volume", VolumeUpdated, self);
729     var_AddCallback(p_playlist, "mute", VolumeUpdated, self);
730
731     if (!OSX_SNOW_LEOPARD) {
732         if ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)
733             var_SetBool(p_playlist, "fullscreen", YES);
734     }
735
736     /* load our Shared Dialogs nib */
737     [NSBundle loadNibNamed:@"SharedDialogs" owner: NSApp];
738
739     /* subscribe to various interactive dialogues */
740     var_Create(p_intf, "dialog-error", VLC_VAR_ADDRESS);
741     var_AddCallback(p_intf, "dialog-error", DialogCallback, self);
742     var_Create(p_intf, "dialog-critical", VLC_VAR_ADDRESS);
743     var_AddCallback(p_intf, "dialog-critical", DialogCallback, self);
744     var_Create(p_intf, "dialog-login", VLC_VAR_ADDRESS);
745     var_AddCallback(p_intf, "dialog-login", DialogCallback, self);
746     var_Create(p_intf, "dialog-question", VLC_VAR_ADDRESS);
747     var_AddCallback(p_intf, "dialog-question", DialogCallback, self);
748     var_Create(p_intf, "dialog-progress-bar", VLC_VAR_ADDRESS);
749     var_AddCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
750     dialog_Register(p_intf);
751
752     /* init Apple Remote support */
753     o_remote = [[AppleRemote alloc] init];
754     [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
755     [o_remote setDelegate: _o_sharedMainInstance];
756
757     /* yeah, we are done */
758     b_nativeFullscreenMode = NO;
759 #ifdef MAC_OS_X_VERSION_10_7
760     if (!OSX_SNOW_LEOPARD)
761         b_nativeFullscreenMode = var_InheritBool(p_intf, "macosx-nativefullscreenmode");
762 #endif
763
764     if (config_GetInt(VLCIntf, "macosx-icon-change")) {
765         /* After day 354 of the year, the usual VLC cone is replaced by another cone
766          * wearing a Father Xmas hat.
767          * Note: this icon doesn't represent an endorsement of The Coca-Cola Company.
768          */
769         NSCalendar *gregorian =
770         [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
771         NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];
772         [gregorian release];
773
774         if (dayOfYear >= 354)
775             [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"vlc-xmas"]];
776     }
777
778     nib_main_loaded = TRUE;
779 }
780
781 - (void)applicationWillFinishLaunching:(NSNotification *)aNotification
782 {
783     playlist_t * p_playlist = pl_Get(VLCIntf);
784     PL_LOCK;
785     items_at_launch = p_playlist->p_local_category->i_children;
786     PL_UNLOCK;
787
788     [NSBundle loadNibNamed:@"MainWindow" owner: self];
789
790     // This cannot be called directly here, as the main loop is not running yet so it would have no effect.
791     // So lets enqueue it into the loop for later execution.
792     [o_mainwindow performSelector:@selector(makeKeyAndOrderFront:) withObject:nil afterDelay:0];
793
794 #ifdef HAVE_SPARKLE
795     [[SUUpdater sharedUpdater] setDelegate:self];
796 #endif
797 }
798
799 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
800 {
801     launched = YES;
802
803     if (!p_intf)
804         return;
805
806     NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleVersion"];
807     NSRange endRande = [appVersion rangeOfString:@"-"];
808     if (endRande.location != NSNotFound)
809         appVersion = [appVersion substringToIndex:endRande.location];
810
811     BWQuincyManager *quincyManager = [BWQuincyManager sharedQuincyManager];
812     [quincyManager setApplicationVersion:appVersion];
813     [quincyManager setSubmissionURL:@"http://crash.videolan.org/crash_v200.php"];
814     [quincyManager setDelegate:self];
815     [quincyManager setCompanyName:@"VideoLAN"];
816
817     [self updateCurrentlyUsedHotkeys];
818
819     /* init media key support */
820     b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys");
821     if (b_mediaKeySupport) {
822         o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
823         [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
824                                                                  [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
825                                                                  nil]];
826     }
827     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name: @"VLCMediaKeySupportSettingChanged" object: nil];
828
829     [self removeOldPreferences];
830
831     /* Handle sleep notification */
832     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
833            name:NSWorkspaceWillSleepNotification object:nil];
834
835     /* update the main window */
836     [o_mainwindow updateWindow];
837     [o_mainwindow updateTimeSlider];
838     [o_mainwindow updateVolumeSlider];
839
840     /* Hack: Playlist is started before the interface.
841      * Thus, call additional updaters as we might miss these events if posted before
842      * the callbacks are registered.
843      */
844     [self PlaylistItemChanged];
845     [self playbackModeUpdated];
846
847     // respect playlist-autostart
848     // note that PLAYLIST_PLAY will not stop any playback if already started
849     playlist_t * p_playlist = pl_Get(VLCIntf);
850     PL_LOCK;
851     BOOL kidsAround = p_playlist->p_local_category->i_children != 0;
852     if (kidsAround && var_GetBool(p_playlist, "playlist-autostart"))
853         playlist_Control(p_playlist, PLAYLIST_PLAY, true);
854     PL_UNLOCK;
855 }
856
857 /* don't allow a double termination call. If the user has
858  * already invoked the quit then simply return this time. */
859 static bool f_appExit = false;
860
861 #pragma mark -
862 #pragma mark Termination
863
864 - (BOOL)isTerminating
865 {
866     return f_appExit;
867 }
868
869 - (void)applicationWillTerminate:(NSNotification *)notification
870 {
871     bool isTerminating;
872
873     [o_appLock lock];
874     isTerminating = f_appExit;
875     f_appExit = true;
876     [o_appLock unlock];
877
878     if (isTerminating)
879         return;
880
881     [self resumeItunesPlayback:nil];
882
883     if (notification == nil)
884         [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
885
886     playlist_t * p_playlist = pl_Get(p_intf);
887
888     /* save current video and audio profiles */
889     [[VLCVideoEffects sharedInstance] saveCurrentProfile];
890     [[VLCAudioEffects sharedInstance] saveCurrentProfile];
891
892     /* Save some interface state in configuration, at module quit */
893     config_PutInt(p_intf, "random", var_GetBool(p_playlist, "random"));
894     config_PutInt(p_intf, "loop", var_GetBool(p_playlist, "loop"));
895     config_PutInt(p_intf, "repeat", var_GetBool(p_playlist, "repeat"));
896
897     msg_Dbg(p_intf, "Terminating");
898
899     /* unsubscribe from the interactive dialogues */
900     dialog_Unregister(p_intf);
901     var_DelCallback(p_intf, "dialog-error", DialogCallback, self);
902     var_DelCallback(p_intf, "dialog-critical", DialogCallback, self);
903     var_DelCallback(p_intf, "dialog-login", DialogCallback, self);
904     var_DelCallback(p_intf, "dialog-question", DialogCallback, self);
905     var_DelCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
906     var_DelCallback(p_playlist, "item-change", PLItemUpdated, self);
907     var_DelCallback(p_playlist, "input-current", PLItemChanged, self);
908     var_DelCallback(p_playlist, "playlist-item-append", PLItemAppended, self);
909     var_DelCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self);
910     var_DelCallback(p_playlist, "random", PlaybackModeUpdated, self);
911     var_DelCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
912     var_DelCallback(p_playlist, "loop", PlaybackModeUpdated, self);
913     var_DelCallback(p_playlist, "volume", VolumeUpdated, self);
914     var_DelCallback(p_playlist, "mute", VolumeUpdated, self);
915     var_DelCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
916     var_DelCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
917     var_DelCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self);
918
919     if (p_current_input) {
920         /* continue playback where you left off */
921         [[self playlist] storePlaybackPositionForItem:p_current_input];
922
923         var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
924         vlc_object_release(p_current_input);
925         p_current_input = NULL;
926     }
927
928     /* remove global observer watching for vout device changes correctly */
929     [[NSNotificationCenter defaultCenter] removeObserver: self];
930
931     [o_vout_provider_lock lock];
932     // release before o_info!
933     // closes all open vouts
934     [o_vout_controller release];
935     o_vout_controller = nil;
936     [o_vout_provider_lock unlock];
937
938     /* release some other objects here, because it isn't sure whether dealloc
939      * will be called later on */
940     if (o_sprefs)
941         [o_sprefs release];
942
943     if (o_prefs)
944         [o_prefs release];
945
946     [o_open release];
947
948     if (o_info)
949         [o_info release];
950
951     if (o_wizard)
952         [o_wizard release];
953
954     if (!o_bookmarks)
955         [o_bookmarks release];
956
957     [o_coredialogs release];
958     [o_eyetv release];
959     [o_remote release];
960
961     /* unsubscribe from libvlc's debug messages */
962     vlc_LogSet(p_intf->p_libvlc, NULL, NULL);
963
964     [o_usedHotkeys release];
965     o_usedHotkeys = NULL;
966
967     [o_mediaKeyController release];
968
969     /* write cached user defaults to disk */
970     [[NSUserDefaults standardUserDefaults] synchronize];
971
972     [o_mainmenu release];
973     [o_coreinteraction release];
974
975     o_mainwindow = NULL;
976
977     [self setIntf:nil];
978 }
979
980 #pragma mark -
981 #pragma mark Sparkle delegate
982
983 #ifdef HAVE_SPARKLE
984 /* received directly before the update gets installed, so let's shut down a bit */
985 - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
986 {
987     [NSApp activateIgnoringOtherApps:YES];
988     [o_remote stopListening: self];
989     [[VLCCoreInteraction sharedInstance] stop];
990 }
991
992 /* don't be enthusiastic about an update if we currently play a video */
993 - (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
994 {
995     if ([self activeVideoPlayback])
996         return NO;
997
998     return YES;
999 }
1000 #endif
1001
1002 #pragma mark -
1003 #pragma mark Media Key support
1004
1005 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
1006 {
1007     if (b_mediaKeySupport) {
1008         assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
1009
1010         int keyCode = (([event data1] & 0xFFFF0000) >> 16);
1011         int keyFlags = ([event data1] & 0x0000FFFF);
1012         int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
1013         int keyRepeat = (keyFlags & 0x1);
1014
1015         if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
1016             [[VLCCoreInteraction sharedInstance] playOrPause];
1017
1018         if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
1019             if (keyState == 0 && keyRepeat == 0)
1020                 [[VLCCoreInteraction sharedInstance] next];
1021             else if (keyRepeat == 1) {
1022                 [[VLCCoreInteraction sharedInstance] forwardShort];
1023                 b_mediakeyJustJumped = YES;
1024                 [self performSelector:@selector(resetMediaKeyJump)
1025                            withObject: NULL
1026                            afterDelay:0.25];
1027             }
1028         }
1029
1030         if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
1031             if (keyState == 0 && keyRepeat == 0)
1032                 [[VLCCoreInteraction sharedInstance] previous];
1033             else if (keyRepeat == 1) {
1034                 [[VLCCoreInteraction sharedInstance] backwardShort];
1035                 b_mediakeyJustJumped = YES;
1036                 [self performSelector:@selector(resetMediaKeyJump)
1037                            withObject: NULL
1038                            afterDelay:0.25];
1039             }
1040         }
1041     }
1042 }
1043
1044 #pragma mark -
1045 #pragma mark Other notification
1046
1047 /* Listen to the remote in exclusive mode, only when VLC is the active
1048    application */
1049 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
1050 {
1051     if (!p_intf)
1052         return;
1053     if (var_InheritBool(p_intf, "macosx-appleremote") == YES)
1054         [o_remote startListening: self];
1055 }
1056 - (void)applicationDidResignActive:(NSNotification *)aNotification
1057 {
1058     if (!p_intf)
1059         return;
1060     [o_remote stopListening: self];
1061 }
1062
1063 /* Triggered when the computer goes to sleep */
1064 - (void)computerWillSleep: (NSNotification *)notification
1065 {
1066     [[VLCCoreInteraction sharedInstance] pause];
1067 }
1068
1069 #pragma mark -
1070 #pragma mark File opening over dock icon
1071
1072 - (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
1073 {
1074     // Only add items here which are getting dropped to to the application icon
1075     // or are given at startup. If a file is passed via command line, libvlccore
1076     // will add the item, but cocoa also calls this function. In this case, the
1077     // invocation is ignored here.
1078     if (launched == NO) {
1079         if (items_at_launch) {
1080             int items = [o_names count];
1081             if (items > items_at_launch)
1082                 items_at_launch = 0;
1083             else
1084                 items_at_launch -= items;
1085             return;
1086         }
1087     }
1088
1089     char *psz_uri = vlc_path2uri([[o_names objectAtIndex:0] UTF8String], NULL);
1090
1091     // try to add file as subtitle
1092     if ([o_names count] == 1 && psz_uri) {
1093         input_thread_t * p_input = pl_CurrentInput(VLCIntf);
1094         if (p_input) {
1095             int i_result = input_AddSubtitleOSD(p_input, [[o_names objectAtIndex:0] UTF8String], true, true);
1096             vlc_object_release(p_input);
1097             if (i_result == VLC_SUCCESS) {
1098                 free(psz_uri);
1099                 return;
1100             }
1101         }
1102     }
1103     free(psz_uri);
1104
1105     NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
1106     NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
1107     for (NSUInteger i = 0; i < [o_sorted_names count]; i++) {
1108         psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file");
1109         if (!psz_uri)
1110             continue;
1111
1112         NSDictionary *o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
1113         free(psz_uri);
1114         [o_result addObject: o_dic];
1115     }
1116
1117     [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result];
1118 }
1119
1120 /* When user click in the Dock icon our double click in the finder */
1121 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
1122 {
1123     if (!hasVisibleWindows)
1124         [o_mainwindow makeKeyAndOrderFront:self];
1125
1126     return YES;
1127 }
1128
1129 #pragma mark -
1130 #pragma mark Apple Remote Control
1131
1132 /* Helper method for the remote control interface in order to trigger forward/backward and volume
1133    increase/decrease as long as the user holds the left/right, plus/minus button */
1134 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1135 {
1136     if (b_remote_button_hold) {
1137         switch([buttonIdentifierNumber intValue]) {
1138             case kRemoteButtonRight_Hold:
1139                 [[VLCCoreInteraction sharedInstance] forward];
1140                 break;
1141             case kRemoteButtonLeft_Hold:
1142                 [[VLCCoreInteraction sharedInstance] backward];
1143                 break;
1144             case kRemoteButtonVolume_Plus_Hold:
1145                 if (p_intf)
1146                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP);
1147                 break;
1148             case kRemoteButtonVolume_Minus_Hold:
1149                 if (p_intf)
1150                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN);
1151                 break;
1152         }
1153         if (b_remote_button_hold) {
1154             /* trigger event */
1155             [self performSelector:@selector(executeHoldActionForRemoteButton:)
1156                          withObject:buttonIdentifierNumber
1157                          afterDelay:0.25];
1158         }
1159     }
1160 }
1161
1162 /* Apple Remote callback */
1163 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1164                pressedDown: (BOOL) pressedDown
1165                 clickCount: (unsigned int) count
1166 {
1167     switch(buttonIdentifier) {
1168         case k2009RemoteButtonFullscreen:
1169             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1170             break;
1171         case k2009RemoteButtonPlay:
1172             [[VLCCoreInteraction sharedInstance] playOrPause];
1173             break;
1174         case kRemoteButtonPlay:
1175             if (count >= 2)
1176                 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1177             else
1178                 [[VLCCoreInteraction sharedInstance] playOrPause];
1179             break;
1180         case kRemoteButtonVolume_Plus:
1181             if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol"))
1182                 [NSSound increaseSystemVolume];
1183             else
1184                 if (p_intf)
1185                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP);
1186             break;
1187         case kRemoteButtonVolume_Minus:
1188             if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol"))
1189                 [NSSound decreaseSystemVolume];
1190             else
1191                 if (p_intf)
1192                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN);
1193             break;
1194         case kRemoteButtonRight:
1195             if (config_GetInt(VLCIntf, "macosx-appleremote-prevnext"))
1196                 [[VLCCoreInteraction sharedInstance] forward];
1197             else
1198                 [[VLCCoreInteraction sharedInstance] next];
1199             break;
1200         case kRemoteButtonLeft:
1201             if (config_GetInt(VLCIntf, "macosx-appleremote-prevnext"))
1202                 [[VLCCoreInteraction sharedInstance] backward];
1203             else
1204                 [[VLCCoreInteraction sharedInstance] previous];
1205             break;
1206         case kRemoteButtonRight_Hold:
1207         case kRemoteButtonLeft_Hold:
1208         case kRemoteButtonVolume_Plus_Hold:
1209         case kRemoteButtonVolume_Minus_Hold:
1210             /* simulate an event as long as the user holds the button */
1211             b_remote_button_hold = pressedDown;
1212             if (pressedDown) {
1213                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1214                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1215                            withObject:buttonIdentifierNumber];
1216             }
1217             break;
1218         case kRemoteButtonMenu:
1219             [o_controls showPosition: self]; //FIXME
1220             break;
1221         case kRemoteButtonPlay_Sleep:
1222         {
1223             NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1224             [script executeAndReturnError:nil];
1225             [script release];
1226             break;
1227         }
1228         default:
1229             /* Add here whatever you want other buttons to do */
1230             break;
1231     }
1232 }
1233
1234 #pragma mark -
1235 #pragma mark Key Shortcuts
1236
1237 /*****************************************************************************
1238  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1239  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1240  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1241  *****************************************************************************/
1242 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1243 {
1244     unichar key = 0;
1245     vlc_value_t val;
1246     unsigned int i_pressed_modifiers = 0;
1247
1248     val.i_int = 0;
1249     i_pressed_modifiers = [o_event modifierFlags];
1250
1251     if (i_pressed_modifiers & NSControlKeyMask)
1252         val.i_int |= KEY_MODIFIER_CTRL;
1253
1254     if (i_pressed_modifiers & NSAlternateKeyMask)
1255         val.i_int |= KEY_MODIFIER_ALT;
1256
1257     if (i_pressed_modifiers & NSShiftKeyMask)
1258         val.i_int |= KEY_MODIFIER_SHIFT;
1259
1260     if (i_pressed_modifiers & NSCommandKeyMask)
1261         val.i_int |= KEY_MODIFIER_COMMAND;
1262
1263     NSString * characters = [o_event charactersIgnoringModifiers];
1264     if ([characters length] > 0) {
1265         key = [[characters lowercaseString] characterAtIndex: 0];
1266
1267         /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1268         if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1269             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1270             return YES;
1271         }
1272
1273         if (!b_force) {
1274             switch(key) {
1275                 case NSDeleteCharacter:
1276                 case NSDeleteFunctionKey:
1277                 case NSDeleteCharFunctionKey:
1278                 case NSBackspaceCharacter:
1279                 case NSUpArrowFunctionKey:
1280                 case NSDownArrowFunctionKey:
1281                 case NSEnterCharacter:
1282                 case NSCarriageReturnCharacter:
1283                     return NO;
1284             }
1285         }
1286
1287         val.i_int |= CocoaKeyToVLC(key);
1288
1289         BOOL b_found_key = NO;
1290         for (NSUInteger i = 0; i < [o_usedHotkeys count]; i++) {
1291             NSString *str = [o_usedHotkeys objectAtIndex:i];
1292             unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1293
1294             if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1295                (i_keyModifiers & NSShiftKeyMask)     == (i_pressed_modifiers & NSShiftKeyMask) &&
1296                (i_keyModifiers & NSControlKeyMask)   == (i_pressed_modifiers & NSControlKeyMask) &&
1297                (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1298                (i_keyModifiers & NSCommandKeyMask)   == (i_pressed_modifiers & NSCommandKeyMask)) {
1299                 b_found_key = YES;
1300                 break;
1301             }
1302         }
1303
1304         if (b_found_key) {
1305             var_SetInteger(p_intf->p_libvlc, "key-pressed", val.i_int);
1306             return YES;
1307         }
1308     }
1309
1310     return NO;
1311 }
1312
1313 - (void)updateCurrentlyUsedHotkeys
1314 {
1315     NSMutableArray *o_tempArray = [[NSMutableArray alloc] init];
1316     /* Get the main Module */
1317     module_t *p_main = module_get_main();
1318     assert(p_main);
1319     unsigned confsize;
1320     module_config_t *p_config;
1321
1322     p_config = module_config_get (p_main, &confsize);
1323
1324     for (size_t i = 0; i < confsize; i++) {
1325         module_config_t *p_item = p_config + i;
1326
1327         if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1328            && !strncmp(p_item->psz_name , "key-", 4)
1329            && !EMPTY_STR(p_item->psz_text)) {
1330             if (p_item->value.psz)
1331                 [o_tempArray addObject: [NSString stringWithUTF8String:p_item->value.psz]];
1332         }
1333     }
1334     module_config_free (p_config);
1335
1336     if (o_usedHotkeys)
1337         [o_usedHotkeys release];
1338     o_usedHotkeys = [[NSArray alloc] initWithArray: o_tempArray copyItems: YES];
1339     [o_tempArray release];
1340 }
1341
1342 #pragma mark -
1343 #pragma mark Interface updaters
1344
1345 - (void)plItemAppended:(NSArray *)o_val
1346 {
1347     int i_node = [[o_val objectAtIndex:0] intValue];
1348     int i_item = [[o_val objectAtIndex:1] intValue];
1349
1350     [[[self playlist] model] addItem:i_item withParentNode:i_node];
1351
1352     // update badge in sidebar
1353     [o_mainwindow updateWindow];
1354
1355     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
1356                                                         object: nil
1357                                                       userInfo: nil];
1358 }
1359
1360 - (void)plItemRemoved:(NSNumber *)o_val
1361 {
1362     int i_item = [o_val intValue];
1363
1364     [[[self playlist] model] removeItem:i_item];
1365     [[self playlist] deletionCompleted];
1366
1367     // update badge in sidebar
1368     [o_mainwindow updateWindow];
1369
1370     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
1371                                                         object: nil
1372                                                       userInfo: nil];
1373 }
1374
1375 // This must be called on main thread
1376 - (void)PlaylistItemChanged
1377 {
1378     input_thread_t *p_input_changed = NULL;
1379
1380     if (p_current_input && p_current_input->b_dead) {
1381         var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
1382         vlc_object_release(p_current_input);
1383         p_current_input = NULL;
1384
1385         [o_mainmenu setRateControlsEnabled: NO];
1386
1387         [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
1388                                                             object:nil];
1389     }
1390     else if (!p_current_input) {
1391         // object is hold here and released then it is dead
1392         p_current_input = playlist_CurrentInput(pl_Get(VLCIntf));
1393         if (p_current_input) {
1394             var_AddCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
1395             [self playbackStatusUpdated];
1396             [o_mainmenu setRateControlsEnabled: YES];
1397
1398             if ([self activeVideoPlayback] && [[o_mainwindow videoView] isHidden]) {
1399                 [o_mainwindow changePlaylistState: psPlaylistItemChangedEvent];
1400             }
1401
1402             p_input_changed = vlc_object_hold(p_current_input);
1403
1404             [[self playlist] currentlyPlayingItemChanged];
1405
1406             [[self playlist] continuePlaybackWhereYouLeftOff:p_current_input];
1407
1408             [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
1409                                                                 object:nil];
1410         }
1411     }
1412
1413     [self updateMetaAndInfo];
1414
1415     [o_mainwindow updateWindow];
1416     [self updateDelays];
1417     [self updateMainMenu];
1418
1419     /*
1420      * Due to constraints within NSAttributedString's main loop runtime handling
1421      * and other issues, we need to inform the extension manager on a separate thread.
1422      * The serial queue ensures that changed inputs are propagated in the same order as they arrive.
1423      */
1424     dispatch_async(informInputChangedQueue, ^{
1425         [[ExtensionsManager getInstance:p_intf] inputChanged:p_input_changed];
1426         if (p_input_changed)
1427             vlc_object_release(p_input_changed);
1428     });
1429 }
1430
1431 - (void)plItemUpdated
1432 {
1433     [o_mainwindow updateName];
1434
1435     if (o_info != NULL)
1436         [o_info updateMetadata];
1437 }
1438
1439 - (void)updateMainMenu
1440 {
1441     [o_mainmenu setupMenus];
1442     [o_mainmenu updatePlaybackRate];
1443     [[VLCCoreInteraction sharedInstance] resetAtoB];
1444 }
1445
1446 - (void)updateMainWindow
1447 {
1448     [o_mainwindow updateWindow];
1449 }
1450
1451 - (void)showMainWindow
1452 {
1453     [o_mainwindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:NO];
1454 }
1455
1456 - (void)showFullscreenController
1457 {
1458     // defer selector here (possibly another time) to ensure that keyWindow is set properly
1459     // (needed for NSApplicationDidBecomeActiveNotification)
1460     [o_mainwindow performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
1461 }
1462
1463 - (void)updateDelays
1464 {
1465     [[VLCTrackSynchronization sharedInstance] performSelectorOnMainThread: @selector(updateValues) withObject: nil waitUntilDone:NO];
1466 }
1467
1468 - (void)updateName
1469 {
1470     [o_mainwindow updateName];
1471 }
1472
1473 - (void)updatePlaybackPosition
1474 {
1475     [o_mainwindow updateTimeSlider];
1476     [[VLCCoreInteraction sharedInstance] updateAtoB];
1477 }
1478
1479 - (void)updateVolume
1480 {
1481     [o_mainwindow updateVolumeSlider];
1482 }
1483
1484 - (void)updateRecordState: (BOOL)b_value
1485 {
1486     [o_mainmenu updateRecordState:b_value];
1487 }
1488
1489 - (void)updateMetaAndInfo
1490 {
1491     if (!p_current_input) {
1492         [[self info] updatePanelWithItem:nil];
1493         return;
1494     }
1495
1496     input_item_t *p_input_item = input_GetItem(p_current_input);
1497
1498     [[[self playlist] model] updateItem:p_input_item];
1499     [[self info] updatePanelWithItem:p_input_item];
1500 }
1501
1502 - (void)resumeItunesPlayback:(id)sender
1503 {
1504     if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) {
1505         if (b_has_itunes_paused) {
1506             iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
1507             if (iTunesApp && [iTunesApp isRunning]) {
1508                 if ([iTunesApp playerState] == iTunesEPlSPaused) {
1509                     msg_Dbg(p_intf, "unpausing iTunes");
1510                     [iTunesApp playpause];
1511                 }
1512             }
1513         }
1514
1515         if (b_has_spotify_paused) {
1516             SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
1517             if (spotifyApp) {
1518                 if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
1519                     if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePaused) {
1520                         msg_Dbg(p_intf, "unpausing Spotify");
1521                         [spotifyApp play];
1522                     }
1523                 }
1524             }
1525         }
1526     }
1527
1528     b_has_itunes_paused = NO;
1529     b_has_spotify_paused = NO;
1530     o_itunes_play_timer = nil;
1531 }
1532
1533 - (void)playbackStatusUpdated
1534 {
1535     int state = -1;
1536     if (p_current_input) {
1537         state = var_GetInteger(p_current_input, "state");
1538     }
1539
1540     int i_control_itunes = var_InheritInteger(p_intf, "macosx-control-itunes");
1541     // cancel itunes timer if next item starts playing
1542     if (state > -1 && state != END_S && i_control_itunes > 0) {
1543         if (o_itunes_play_timer) {
1544             [o_itunes_play_timer invalidate];
1545             o_itunes_play_timer = nil;
1546         }
1547     }
1548
1549     if (state == PLAYING_S) {
1550         if (i_control_itunes > 0) {
1551             // pause iTunes
1552             if (!b_has_itunes_paused) {
1553                 iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
1554                 if (iTunesApp && [iTunesApp isRunning]) {
1555                     if ([iTunesApp playerState] == iTunesEPlSPlaying) {
1556                         msg_Dbg(p_intf, "pausing iTunes");
1557                         [iTunesApp pause];
1558                         b_has_itunes_paused = YES;
1559                     }
1560                 }
1561             }
1562
1563             // pause Spotify
1564             if (!b_has_spotify_paused) {
1565                 SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
1566
1567                 if (spotifyApp) {
1568                     if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
1569                         if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePlaying) {
1570                             msg_Dbg(p_intf, "pausing Spotify");
1571                             [spotifyApp pause];
1572                             b_has_spotify_paused = YES;
1573                         }
1574                     }
1575                 }
1576             }
1577         }
1578
1579         /* Declare user activity.
1580          This wakes the display if it is off, and postpones display sleep according to the users system preferences
1581          Available from 10.7.3 */
1582 #ifdef MAC_OS_X_VERSION_10_7
1583         if ([self activeVideoPlayback] && IOPMAssertionDeclareUserActivity)
1584         {
1585             CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
1586             IOPMAssertionDeclareUserActivity(reasonForActivity,
1587                                              kIOPMUserActiveLocal,
1588                                              &userActivityAssertionID);
1589             CFRelease(reasonForActivity);
1590         }
1591 #endif
1592
1593         /* prevent the system from sleeping */
1594         if (systemSleepAssertionID > 0) {
1595             msg_Dbg(VLCIntf, "releasing old sleep blocker (%i)" , systemSleepAssertionID);
1596             IOPMAssertionRelease(systemSleepAssertionID);
1597         }
1598
1599         IOReturn success;
1600         /* work-around a bug in 10.7.4 and 10.7.5, so check for 10.7.x < 10.7.4, 10.8 and 10.6 */
1601         if ((NSAppKitVersionNumber >= 1115.2 && NSAppKitVersionNumber < 1138.45) || OSX_MOUNTAIN_LION || OSX_MAVERICKS || OSX_YOSEMITE || OSX_SNOW_LEOPARD) {
1602             CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
1603             if ([self activeVideoPlayback])
1604                 success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
1605             else
1606                 success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
1607             CFRelease(reasonForActivity);
1608         } else {
1609             /* fall-back on the 10.5 mode, which also works on 10.7.4 and 10.7.5 */
1610             if ([self activeVideoPlayback])
1611                 success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
1612             else
1613                 success = IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
1614         }
1615
1616         if (success == kIOReturnSuccess)
1617             msg_Dbg(VLCIntf, "prevented sleep through IOKit (%i)", systemSleepAssertionID);
1618         else
1619             msg_Warn(VLCIntf, "failed to prevent system sleep through IOKit");
1620
1621         [[self mainMenu] setPause];
1622         [o_mainwindow setPause];
1623     } else {
1624         [o_mainmenu setSubmenusEnabled: FALSE];
1625         [[self mainMenu] setPlay];
1626         [o_mainwindow setPlay];
1627
1628         /* allow the system to sleep again */
1629         if (systemSleepAssertionID > 0) {
1630             msg_Dbg(VLCIntf, "releasing sleep blocker (%i)" , systemSleepAssertionID);
1631             IOPMAssertionRelease(systemSleepAssertionID);
1632         }
1633
1634         if (state == END_S || state == -1) {
1635             /* continue playback where you left off */
1636             if (p_current_input)
1637                 [[self playlist] storePlaybackPositionForItem:p_current_input];
1638
1639             if (i_control_itunes > 0) {
1640                 if (o_itunes_play_timer) {
1641                     [o_itunes_play_timer invalidate];
1642                 }
1643                 o_itunes_play_timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
1644                                                                        target: self
1645                                                                      selector: @selector(resumeItunesPlayback:)
1646                                                                      userInfo: nil
1647                                                                       repeats: NO];
1648             }
1649         }
1650     }
1651
1652     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
1653     [self performSelectorOnMainThread:@selector(sendDistributedNotificationWithUpdatedPlaybackStatus) withObject: nil waitUntilDone: NO];
1654 }
1655
1656 - (void)sendDistributedNotificationWithUpdatedPlaybackStatus
1657 {
1658     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
1659                                                                    object:nil
1660                                                                  userInfo:nil
1661                                                        deliverImmediately:YES];
1662 }
1663
1664 - (void)playbackModeUpdated
1665 {
1666     playlist_t * p_playlist = pl_Get(VLCIntf);
1667
1668     bool loop = var_GetBool(p_playlist, "loop");
1669     bool repeat = var_GetBool(p_playlist, "repeat");
1670     if (repeat) {
1671         [[o_mainwindow controlsBar] setRepeatOne];
1672         [o_mainmenu setRepeatOne];
1673     } else if (loop) {
1674         [[o_mainwindow controlsBar] setRepeatAll];
1675         [o_mainmenu setRepeatAll];
1676     } else {
1677         [[o_mainwindow controlsBar] setRepeatOff];
1678         [o_mainmenu setRepeatOff];
1679     }
1680
1681     [[o_mainwindow controlsBar] setShuffle];
1682     [o_mainmenu setShuffle];
1683 }
1684
1685 #pragma mark -
1686 #pragma mark Window updater
1687
1688 - (void)setActiveVideoPlayback:(BOOL)b_value
1689 {
1690     assert([NSThread isMainThread]);
1691
1692     b_active_videoplayback = b_value;
1693     if (o_mainwindow) {
1694         [o_mainwindow setVideoplayEnabled];
1695     }
1696
1697     // update sleep blockers
1698     [self playbackStatusUpdated];
1699 }
1700
1701 #pragma mark -
1702 #pragma mark Other objects getters
1703
1704 - (id)mainMenu
1705 {
1706     return o_mainmenu;
1707 }
1708
1709 - (VLCMainWindow *)mainWindow
1710 {
1711     return o_mainwindow;
1712 }
1713
1714 - (id)controls
1715 {
1716     return o_controls;
1717 }
1718
1719 - (id)bookmarks
1720 {
1721     if (!o_bookmarks)
1722         o_bookmarks = [[VLCBookmarks alloc] init];
1723
1724     if (!nib_bookmarks_loaded)
1725         nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner: NSApp];
1726
1727     return o_bookmarks;
1728 }
1729
1730 - (id)open
1731 {
1732     if (!nib_open_loaded)
1733         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
1734
1735     return o_open;
1736 }
1737
1738 - (id)simplePreferences
1739 {
1740     if (!o_sprefs)
1741         o_sprefs = [[VLCSimplePrefs alloc] init];
1742
1743     if (!nib_prefs_loaded)
1744         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1745
1746     return o_sprefs;
1747 }
1748
1749 - (id)preferences
1750 {
1751     if (!o_prefs)
1752         o_prefs = [[VLCPrefs alloc] init];
1753
1754     if (!nib_prefs_loaded)
1755         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1756
1757     return o_prefs;
1758 }
1759
1760 - (id)playlist
1761 {
1762     return o_playlist;
1763 }
1764
1765 - (id)info
1766 {
1767     if (!o_info)
1768         o_info = [[VLCInfo alloc] init];
1769
1770     if (! nib_info_loaded)
1771         nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: NSApp];
1772
1773     return o_info;
1774 }
1775
1776 - (id)wizard
1777 {
1778     if (!o_wizard)
1779         o_wizard = [[VLCWizard alloc] init];
1780
1781     if (!nib_wizard_loaded) {
1782         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp];
1783         [o_wizard initStrings];
1784     }
1785
1786     return o_wizard;
1787 }
1788
1789 - (id)coreDialogProvider
1790 {
1791     if (!nib_coredialogs_loaded) {
1792         nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: NSApp];
1793     }
1794
1795     return o_coredialogs;
1796 }
1797
1798 - (id)eyeTVController
1799 {
1800     return o_eyetv;
1801 }
1802
1803 - (id)appleRemoteController
1804 {
1805     return o_remote;
1806 }
1807
1808 - (BOOL)activeVideoPlayback
1809 {
1810     return b_active_videoplayback;
1811 }
1812
1813 #pragma mark -
1814 #pragma mark Remove old prefs
1815
1816
1817 static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion";
1818 static const int kCurrentPreferencesVersion = 3;
1819
1820 + (void)initialize
1821 {
1822     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCurrentPreferencesVersion]
1823                                                             forKey:kVLCPreferencesVersion];
1824
1825     [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
1826 }
1827
1828 - (void)resetAndReinitializeUserDefaults
1829 {
1830     // note that [NSUserDefaults resetStandardUserDefaults] will NOT correctly reset to the defaults
1831
1832     NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
1833     [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
1834
1835     // set correct version to avoid question about outdated config
1836     [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1837     [[NSUserDefaults standardUserDefaults] synchronize];
1838 }
1839
1840 - (void)removeOldPreferences
1841 {
1842     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
1843     int version = [defaults integerForKey:kVLCPreferencesVersion];
1844
1845     /*
1846      * Store version explicitely in file, for ease of debugging.
1847      * Otherwise, the value will be just defined at app startup,
1848      * as initialized above.
1849      */
1850     [defaults setInteger:version forKey:kVLCPreferencesVersion];
1851     if (version >= kCurrentPreferencesVersion)
1852         return;
1853
1854     if (version == 1) {
1855         [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1856         [defaults synchronize];
1857
1858         if (![[VLCCoreInteraction sharedInstance] fixPreferences])
1859             return;
1860         else
1861             config_SaveConfigFile(VLCIntf); // we need to do manually, since we won't quit libvlc cleanly
1862     } else if (version == 2) {
1863         /* version 2 (used by VLC 2.0.x and early versions of 2.1) can lead to exceptions within 2.1 or later
1864          * so we reset the OS X specific prefs here - in practice, no user will notice */
1865         [self resetAndReinitializeUserDefaults];
1866
1867     } else {
1868         NSArray *libraries = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
1869             NSUserDomainMask, YES);
1870         if (!libraries || [libraries count] == 0) return;
1871         NSString * preferences = [[libraries objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
1872
1873         int res = NSRunInformationalAlertPanel(_NS("Remove old preferences?"),
1874                     _NS("We just found an older version of VLC's preferences files."),
1875                     _NS("Move To Trash and Relaunch VLC"), _NS("Ignore"), nil, nil);
1876         if (res != NSOKButton) {
1877             [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1878             return;
1879         }
1880
1881         // Do NOT add the current plist file here as this would conflict with caching.
1882         // Instead, just reset below.
1883         NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc", @"VLC", nil];
1884
1885         /* Move the file to trash one by one. Using above array the method would stop after first file
1886            not found. */
1887         for (NSString *file in ourPreferences) {
1888             [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:@"" files:[NSArray arrayWithObject:file] tag:nil];
1889         }
1890
1891         [self resetAndReinitializeUserDefaults];
1892     }
1893
1894     /* Relaunch now */
1895     const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];
1896
1897     /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
1898     if (fork() != 0) {
1899         exit(0);
1900     }
1901     execl(path, path, NULL);
1902 }
1903
1904 #pragma mark -
1905 #pragma mark Playlist toggling
1906
1907 - (void)updateTogglePlaylistState
1908 {
1909     [[self playlist] outlineViewSelectionDidChange: NULL];
1910 }
1911
1912 #pragma mark -
1913
1914 @end
1915
1916 @implementation VLCMain (Internal)
1917
1918 - (void)resetMediaKeyJump
1919 {
1920     b_mediakeyJustJumped = NO;
1921 }
1922
1923 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
1924 {
1925     b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys");
1926     if (b_mediaKeySupport && !o_mediaKeyController)
1927         o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
1928
1929     if (b_mediaKeySupport && ([[[[VLCMain sharedInstance] playlist] model] hasChildren] ||
1930                               p_current_input)) {
1931         if (!b_mediaKeyTrapEnabled) {
1932             b_mediaKeyTrapEnabled = YES;
1933             msg_Dbg(p_intf, "Enable media key support");
1934             [o_mediaKeyController startWatchingMediaKeys];
1935         }
1936     } else {
1937         if (b_mediaKeyTrapEnabled) {
1938             b_mediaKeyTrapEnabled = NO;
1939             msg_Dbg(p_intf, "Disable media key support");
1940             [o_mediaKeyController stopWatchingMediaKeys];
1941         }
1942     }
1943 }
1944
1945 @end
1946
1947 /*****************************************************************************
1948  * VLCApplication interface
1949  *****************************************************************************/
1950
1951 @implementation VLCApplication
1952 // when user selects the quit menu from dock it sends a terminate:
1953 // but we need to send a stop: to properly exits libvlc.
1954 // However, we are not able to change the action-method sent by this standard menu item.
1955 // thus we override terminate: to send a stop:
1956 // see [af97f24d528acab89969d6541d83f17ce1ecd580] that introduced the removal of setjmp() and longjmp()
1957 - (void)terminate:(id)sender
1958 {
1959     [self activateIgnoringOtherApps:YES];
1960     [self stop:sender];
1961 }
1962
1963 @end