intf.m 19.2 KB
Newer Older
1
/*****************************************************************************
2
 * intf.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2015 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Derk-Jan Hartman <hartman at videolan.org>
8
 *          Felix Paul Kühne <fkuehne at videolan dot org>
9
 *          Pierre d'Herbemont <pdherbemont # videolan org>
10
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
16
 *
17 18 19 20 21 22 23
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
30 31 32 33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34 35
#import "intf.h"

36 37
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>
hartman's avatar
hartman committed
38
#include <vlc_common.h>
39
#include <vlc_atomic.h>
40
#include <vlc_keys.h>
41
#include <vlc_dialog.h>
42
#include <vlc_url.h>
43 44
#include <vlc_variables.h>

45
#import "CompatibilityFixes.h"
46
#import "InputManager.h"
47
#import "MainMenu.h"
48
#import "VideoView.h"
49
#import "prefs.h"
50 51
#import "VLCPlaylist.h"
#import "VLCPlaylistInfo.h"
52
#import "VLCPlaylistInfo.h"
53
#import "open.h"
54
#import "VLCBookmarksWindowController.h"
55
#import "VLCCoreDialogProvider.h"
56
#import "simple_prefs.h"
57
#import "CoreInteraction.h"
58
#import "TrackSynchronization.h"
59
#import "ExtensionsManager.h"
60
#import "BWQuincyManager.h"
61
#import "ResumeDialogController.h"
62
#import "DebugMessageVisualizer.h"
63
#import "ConvertAndSave.h"
64

65
#import "VideoEffects.h"
66
#import "AudioEffects.h"
67
#import "intf-prefs.h"
68

69
#ifdef HAVE_SPARKLE
Rafaël Carré's avatar
Rafaël Carré committed
70
#import <Sparkle/Sparkle.h>                 /* we're the update delegate */
71
#endif
72

73
#pragma mark -
74
#pragma mark VLC Interface Object Callbacks
75

76 77 78
/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
79 80 81 82 83 84 85 86

static intf_thread_t *p_interface_thread;

intf_thread_t *getIntf()
{
    return p_interface_thread;
}

87
int OpenIntf (vlc_object_t *p_this)
Jérome Decoodt's avatar
Jérome Decoodt committed
88
{
89 90
    @autoreleasepool {
        intf_thread_t *p_intf = (intf_thread_t*) p_this;
91
        p_interface_thread = p_intf;
92
        msg_Dbg(p_intf, "Starting macosx interface");
93

94
        [VLCApplication sharedApplication];
95
        [VLCMain sharedInstance];
96

97
        [NSBundle loadNibNamed:@"MainMenu" owner:[[VLCMain sharedInstance] mainMenu]];
Goran Dokic's avatar
Goran Dokic committed
98
        // if statusbar enabled in preferences
99
        if (var_InheritBool(p_intf, "macosx-statusicon")) {
Goran Dokic's avatar
Goran Dokic committed
100 101
            [NSBundle loadNibNamed:@"VLCStatusBarIconMainMenu" owner:[[VLCMain sharedInstance] statusBarIcon]];
        }
102
        [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:nil];
103

104
        msg_Dbg(p_intf, "Finished loading macosx interface");
105 106
        return VLC_SUCCESS;
    }
107 108
}

109 110
void CloseIntf (vlc_object_t *p_this)
{
111 112 113
    @autoreleasepool {
        msg_Dbg(p_this, "Closing macosx interface");
        [[VLCMain sharedInstance] applicationWillTerminate:nil];
114
        [VLCMain killInstance];
115 116

        p_interface_thread = nil;
117
    }
118 119
}

120 121
#pragma mark -
#pragma mark Variables Callback
122

123 124 125 126 127
/*****************************************************************************
 * ShowController: Callback triggered by the show-intf playlist variable
 * through the ShowIntf-control-intf, to let us show the controller-win;
 * usually when in fullscreen-mode
 *****************************************************************************/
128 129
static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param)
130
{
131 132 133
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{

David's avatar
David committed
134
            intf_thread_t * p_intf = getIntf();
135 136 137 138 139 140 141 142 143
            if (p_intf) {
                playlist_t * p_playlist = pl_Get(p_intf);
                BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen");
                if (b_fullscreen)
                    [[VLCMain sharedInstance] showFullscreenController];

                else if (!strcmp(psz_variable, "intf-show"))
                    [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:nil];
            }
David's avatar
David committed
144

145
        });
146

147 148
        return VLC_SUCCESS;
    }
149 150
}

151
#pragma mark -
152 153
#pragma mark Private

David's avatar
David committed
154 155 156 157 158
@interface VLCMain () <BWQuincyManagerDelegate
#ifdef HAVE_SPARKLE
    , SUUpdaterDelegate
#endif
>
159
{
160 161 162
    intf_thread_t *p_intf;
    BOOL launched;
    int items_at_launch;
163 164 165

    BOOL b_active_videoplayback;

166
    NSWindowController *_mainWindowController;
167 168 169 170 171
    VLCMainMenu *_mainmenu;
    VLCPrefs *_prefs;
    VLCSimplePrefs *_sprefs;
    VLCOpen *_open;
    VLCCoreDialogProvider *_coredialogs;
172
    VLCBookmarksWindowController *_bookmarks;
173 174 175
    VLCCoreInteraction *_coreinteraction;
    ResumeDialogController *_resume_dialog;
    VLCInputManager *_input_manager;
176
    VLCPlaylist *_playlist;
177
    VLCDebugMessageVisualizer *_messagePanelController;
Goran Dokic's avatar
Goran Dokic committed
178
    VLCStatusBarIcon *_statusBarIcon;
179
    VLCTrackSynchronization *_trackSyncPanel;
180
    VLCAudioEffects *_audioEffectsPanel;
181
    VLCVideoEffects *_videoEffectsPanel;
182
    VLCConvertAndSave *_convertAndSaveWindow;
183
    ExtensionsManager *_extensionsManager;
184
    VLCInfo *_currentMediaInfoPanel;
185

186
    bool b_intf_terminating; /* Makes sure applicationWillTerminate will be called only once */
187
}
188

189
@end
190

191
/*****************************************************************************
Jérome Decoodt's avatar
Jérome Decoodt committed
192
 * VLCMain implementation
193 194 195
 *****************************************************************************/
@implementation VLCMain

196 197 198
#pragma mark -
#pragma mark Initialization

199 200
static VLCMain *sharedInstance = nil;

201
+ (VLCMain *)sharedInstance;
hartman's avatar
hartman committed
202
{
203 204
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
205
        sharedInstance = [[VLCMain alloc] init];
206 207 208
    });

    return sharedInstance;
hartman's avatar
hartman committed
209 210
}

211 212 213 214 215
+ (void)killInstance
{
    sharedInstance = nil;
}

216
- (id)init
hartman's avatar
hartman committed
217
{
218
    self = [super init];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
219
    if (self) {
220
        p_intf = getIntf();
221

Felix Paul Kühne's avatar
Felix Paul Kühne committed
222
        [VLCApplication sharedApplication].delegate = self;
223

224
        _input_manager = [[VLCInputManager alloc] initWithMain:self];
225 226 227

        // first initalize extensions dialog provider, then core dialog
        // provider which will register both at the core
228
        _extensionsManager = [[ExtensionsManager alloc] init];
229
        _coredialogs = [[VLCCoreDialogProvider alloc] init];
230

231
        _mainmenu = [[VLCMainMenu alloc] init];
Goran Dokic's avatar
Goran Dokic committed
232 233

        // if statusbar enabled in preferences
234
        if (var_InheritBool(p_intf, "macosx-statusicon")) {
Goran Dokic's avatar
Goran Dokic committed
235 236 237
            _statusBarIcon = [[VLCStatusBarIcon  alloc] init];
        }

238 239 240 241 242 243 244
        _voutController = [[VLCVoutWindowController alloc] init];
        _playlist = [[VLCPlaylist alloc] init];

        _mainWindowController = [[NSWindowController alloc] initWithWindowNibName:@"MainWindow"];

        var_Create(p_intf, "intf-change", VLC_VAR_BOOL);

245 246
        var_AddCallback(p_intf->obj.libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
        var_AddCallback(p_intf->obj.libvlc, "intf-show", ShowController, (__bridge void *)self);
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

        playlist_t *p_playlist = pl_Get(p_intf);
        if ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)
            var_SetBool(p_playlist, "fullscreen", YES);

        _nativeFullscreenMode = var_InheritBool(p_intf, "macosx-nativefullscreenmode");

        if (var_InheritInteger(p_intf, "macosx-icon-change")) {
            /* After day 354 of the year, the usual VLC cone is replaced by another cone
             * wearing a Father Xmas hat.
             * Note: this icon doesn't represent an endorsement of The Coca-Cola Company.
             */
            NSCalendar *gregorian =
            [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
            NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];

            if (dayOfYear >= 354)
                [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"vlc-xmas"]];
        }

Felix Paul Kühne's avatar
Felix Paul Kühne committed
267 268 269 270 271
        /* announce our launch to a potential eyetv plugin */
        [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
                                                                       object: @"VLCEyeTVSupport"
                                                                     userInfo: NULL
                                                           deliverImmediately: YES];
272

Felix Paul Kühne's avatar
Felix Paul Kühne committed
273
    }
274

275
    return self;
hartman's avatar
hartman committed
276 277
}

278
- (void)dealloc
279
{
David's avatar
David committed
280
    msg_Dbg(getIntf(), "Deinitializing VLCMain object");
hartman's avatar
hartman committed
281 282
}

283 284 285 286
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
    _coreinteraction = [VLCCoreInteraction sharedInstance];

David's avatar
David committed
287
    playlist_t * p_playlist = pl_Get(getIntf());
288 289 290 291 292 293 294 295 296
    PL_LOCK;
    items_at_launch = p_playlist->p_local_category->i_children;
    PL_UNLOCK;

#ifdef HAVE_SPARKLE
    [[SUUpdater sharedUpdater] setDelegate:self];
#endif
}

297
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
298
{
Rafaël Carré's avatar
Rafaël Carré committed
299 300
    launched = YES;

301 302
    if (!p_intf)
        return;
303

304 305 306 307 308 309 310 311 312 313 314
    NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleVersion"];
    NSRange endRande = [appVersion rangeOfString:@"-"];
    if (endRande.location != NSNotFound)
        appVersion = [appVersion substringToIndex:endRande.location];

    BWQuincyManager *quincyManager = [BWQuincyManager sharedQuincyManager];
    [quincyManager setApplicationVersion:appVersion];
    [quincyManager setSubmissionURL:@"http://crash.videolan.org/crash_v200.php"];
    [quincyManager setDelegate:self];
    [quincyManager setCompanyName:@"VideoLAN"];

315
    [_coreinteraction updateCurrentlyUsedHotkeys];
316

317
    [self removeOldPreferences];
318

319 320 321 322
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];

323
    /* update the main window */
324 325 326
    [[self mainWindow] updateWindow];
    [[self mainWindow] updateTimeSlider];
    [[self mainWindow] updateVolumeSlider];
327

328 329
    // respect playlist-autostart
    // note that PLAYLIST_PLAY will not stop any playback if already started
David's avatar
David committed
330
    playlist_t * p_playlist = pl_Get(getIntf());
331
    PL_LOCK;
332
    BOOL kidsAround = p_playlist->p_local_category->i_children != 0;
333
    if (kidsAround && var_GetBool(p_playlist, "playlist-autostart"))
334 335
        playlist_Control(p_playlist, PLAYLIST_PLAY, true);
    PL_UNLOCK;
336 337
}

338 339 340
#pragma mark -
#pragma mark Termination

341 342
- (BOOL)isTerminating
{
343
    return b_intf_terminating;
344 345
}

346
- (void)applicationWillTerminate:(NSNotification *)notification
347
{
348
    if (b_intf_terminating)
349
        return;
350
    b_intf_terminating = true;
351

352
    [_input_manager resumeItunesPlayback:nil];
353

354 355
    if (notification == nil)
        [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
356

357
    playlist_t * p_playlist = pl_Get(p_intf);
358

359
    /* save current video and audio profiles */
360
    [[self videoEffectsPanel] saveCurrentProfile];
361
    [[self audioEffectsPanel] saveCurrentProfile];
362

363
    /* Save some interface state in configuration, at module quit */
364 365 366
    config_PutInt(p_intf, "random", var_GetBool(p_playlist, "random"));
    config_PutInt(p_intf, "loop", var_GetBool(p_playlist, "loop"));
    config_PutInt(p_intf, "repeat", var_GetBool(p_playlist, "repeat"));
367

368
    msg_Dbg(p_intf, "Terminating");
369

370 371
    var_DelCallback(p_intf->obj.libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
    var_DelCallback(p_intf->obj.libvlc, "intf-show", ShowController, (__bridge void *)self);
372

373
    [[NSNotificationCenter defaultCenter] removeObserver: self];
Jérome Decoodt's avatar
Jérome Decoodt committed
374

375
    [_voutController.lock lock];
376
    // closes all open vouts
377
    _voutController = nil;
378
    [_voutController.lock unlock];
379

380 381
    /* write cached user defaults to disk */
    [[NSUserDefaults standardUserDefaults] synchronize];
hartman's avatar
hartman committed
382 383
}

384 385
#pragma mark -
#pragma mark Sparkle delegate
386

387
#ifdef HAVE_SPARKLE
388 389 390
/* received directly before the update gets installed, so let's shut down a bit */
- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
{
391
    [NSApp activateIgnoringOtherApps:YES];
392 393
    [_coreinteraction stopListeningWithAppleRemote];
    [_coreinteraction stop];
394 395
}

396 397 398 399 400 401 402 403
/* don't be enthusiastic about an update if we currently play a video */
- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
{
    if ([self activeVideoPlayback])
        return NO;

    return YES;
}
404
#endif
405

406 407 408
#pragma mark -
#pragma mark Other notification

409 410 411 412
/* Listen to the remote in exclusive mode, only when VLC is the active
   application */
- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
413 414 415
    if (!p_intf)
        return;
    if (var_InheritBool(p_intf, "macosx-appleremote") == YES)
416
        [_coreinteraction startListeningWithAppleRemote];
417 418 419
}
- (void)applicationDidResignActive:(NSNotification *)aNotification
{
420 421
    if (!p_intf)
        return;
422
    [_coreinteraction stopListeningWithAppleRemote];
423 424
}

425 426 427
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
428
    [_coreinteraction pause];
429 430
}

431
#pragma mark -
432
#pragma mark File opening over dock icon
433

434
- (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
435
{
436 437 438 439
    // Only add items here which are getting dropped to to the application icon
    // or are given at startup. If a file is passed via command line, libvlccore
    // will add the item, but cocoa also calls this function. In this case, the
    // invocation is ignored here.
Rafaël Carré's avatar
Rafaël Carré committed
440 441 442 443 444 445 446 447 448 449 450
    if (launched == NO) {
        if (items_at_launch) {
            int items = [o_names count];
            if (items > items_at_launch)
                items_at_launch = 0;
            else
                items_at_launch -= items;
            return;
        }
    }

451
    char *psz_uri = vlc_path2uri([[o_names firstObject] UTF8String], NULL);
David's avatar
David committed
452

453
    // try to add file as subtitle
454
    if ([o_names count] == 1 && psz_uri) {
David's avatar
David committed
455
        input_thread_t * p_input = pl_CurrentInput(getIntf());
456
        if (p_input) {
457
            int i_result = input_AddSubtitleOSD(p_input, [[o_names firstObject] UTF8String], true, true);
458
            vlc_object_release(p_input);
459
            if (i_result == VLC_SUCCESS) {
460
                free(psz_uri);
461
                return;
462
            }
463 464
        }
    }
465
    free(psz_uri);
466

467 468
    NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
    NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
469
    for (NSUInteger i = 0; i < [o_sorted_names count]; i++) {
470
        psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file");
471
        if (!psz_uri)
472
            continue;
473

474
        NSDictionary *o_dic = [NSDictionary dictionaryWithObject:toNSStr(psz_uri) forKey:@"ITEM_URL"];
475
        free(psz_uri);
476
        [o_result addObject: o_dic];
477
    }
478

479
    [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result];
480 481 482 483
}

/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
484
{
485
    if (!hasVisibleWindows)
486
        [[self mainWindow] makeKeyAndOrderFront:self];
487 488 489 490

    return YES;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
491 492
- (void)showFullscreenController
{
493 494
    // defer selector here (possibly another time) to ensure that keyWindow is set properly
    // (needed for NSApplicationDidBecomeActiveNotification)
495
    [[self mainWindow] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
496 497
}

498 499
- (void)setActiveVideoPlayback:(BOOL)b_value
{
500 501
    assert([NSThread isMainThread]);

502
    b_active_videoplayback = b_value;
503 504
    if ([self mainWindow]) {
        [[self mainWindow] setVideoplayEnabled];
505
    }
506 507

    // update sleep blockers
508
    [_input_manager playbackStatusUpdated];
509 510
}

511 512 513
#pragma mark -
#pragma mark Other objects getters

514
- (VLCMainMenu *)mainMenu
515
{
516
    return _mainmenu;
517 518
}

Goran Dokic's avatar
Goran Dokic committed
519 520 521 522 523
- (VLCStatusBarIcon *)statusBarIcon
{
    return _statusBarIcon;
}

524
- (VLCMainWindow *)mainWindow
525
{
526
    return (VLCMainWindow *)[_mainWindowController window];
527 528
}

529
- (VLCInputManager *)inputManager
530
{
531 532 533
    return _input_manager;
}

534 535 536 537 538
- (ExtensionsManager *)extensionsManager
{
    return _extensionsManager;
}

539 540 541 542 543 544 545 546
- (VLCDebugMessageVisualizer *)debugMsgPanel
{
    if (!_messagePanelController)
        _messagePanelController = [[VLCDebugMessageVisualizer alloc] init];

    return _messagePanelController;
}

547 548 549 550 551 552 553 554
- (VLCTrackSynchronization *)trackSyncPanel
{
    if (!_trackSyncPanel)
        _trackSyncPanel = [[VLCTrackSynchronization alloc] init];

    return _trackSyncPanel;
}

555 556 557 558 559 560 561 562
- (VLCAudioEffects *)audioEffectsPanel
{
    if (!_audioEffectsPanel)
        _audioEffectsPanel = [[VLCAudioEffects alloc] init];

    return _audioEffectsPanel;
}

563 564 565 566 567 568 569 570
- (VLCVideoEffects *)videoEffectsPanel
{
    if (!_videoEffectsPanel)
        _videoEffectsPanel = [[VLCVideoEffects alloc] init];

    return _videoEffectsPanel;
}

571 572 573 574 575 576 577 578
- (VLCInfo *)currentMediaInfoPanel;
{
    if (!_currentMediaInfoPanel)
        _currentMediaInfoPanel = [[VLCInfo alloc] init];

    return _currentMediaInfoPanel;
}

579
- (VLCBookmarksWindowController *)bookmarks
580 581
{
    if (!_bookmarks)
582
        _bookmarks = [[VLCBookmarksWindowController alloc] init];
583

584
    return _bookmarks;
585 586
}

587
- (VLCOpen *)open
588
{
589 590
    if (!_open)
        _open = [[VLCOpen alloc] init];
591

592
    return _open;
593 594
}

595 596 597 598 599 600 601 602
- (VLCConvertAndSave *)convertAndSaveWindow
{
    if (_convertAndSaveWindow == nil)
        _convertAndSaveWindow = [[VLCConvertAndSave alloc] init];

    return _convertAndSaveWindow;
}

603
- (VLCSimplePrefs *)simplePreferences
604
{
605 606
    if (!_sprefs)
        _sprefs = [[VLCSimplePrefs alloc] init];
607

608
    return _sprefs;
609 610
}

611
- (VLCPrefs *)preferences
612
{
613 614
    if (!_prefs)
        _prefs = [[VLCPrefs alloc] init];
615

616
    return _prefs;
617 618
}

619
- (VLCPlaylist *)playlist
hartman's avatar
hartman committed
620
{
621
    return _playlist;
hartman's avatar
hartman committed
622 623
}

624
- (VLCCoreDialogProvider *)coreDialogProvider
625
{
626
    return _coredialogs;
627 628
}

629 630
- (ResumeDialogController *)resumeDialog
{
631 632
    if (!_resume_dialog)
        _resume_dialog = [[ResumeDialogController alloc] init];
633

634
    return _resume_dialog;
635 636
}

637
- (BOOL)activeVideoPlayback
638
{
639
    return b_active_videoplayback;
hartman's avatar
hartman committed
640 641
}

642
@end
643

644 645 646
/*****************************************************************************
 * VLCApplication interface
 *****************************************************************************/
647

648
@implementation VLCApplication
649 650 651
// when user selects the quit menu from dock it sends a terminate:
// but we need to send a stop: to properly exits libvlc.
// However, we are not able to change the action-method sent by this standard menu item.
652
// thus we override terminate: to send a stop:
653
// see [af97f24d528acab89969d6541d83f17ce1ecd580] that introduced the removal of setjmp() and longjmp()
654 655
- (void)terminate:(id)sender
{
656
    [self activateIgnoringOtherApps:YES];
657 658 659
    [self stop:sender];
}

660
@end