intf.m 72.3 KB
Newer Older
1
/*****************************************************************************
2
 * intf.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2008 the VideoLAN team
5
 * $Id$
6 7 8
 *
 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
 *          Christophe Massiot <massiot@via.ecp.fr>
9
 *          Derk-Jan Hartman <hartman at videolan.org>
10
 *          Felix Paul Kühne <fkuehne at videolan dot org>
11 12 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
Antoine Cellerier's avatar
Antoine Cellerier committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29 30 31 32
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <sys/param.h>                                    /* for MAXPATHLEN */
#include <string.h>
33
#include <vlc_keys.h>
34

Rafaël Carré's avatar
Rafaël Carré committed
35 36 37 38
#ifdef HAVE_CONFIG_H
#   include "config.h"
#endif

39 40 41 42 43
#import "intf.h"
#import "fspanel.h"
#import "vout.h"
#import "prefs.h"
#import "playlist.h"
44
#import "playlistinfo.h"
45 46 47 48 49 50 51 52 53 54
#import "controls.h"
#import "about.h"
#import "open.h"
#import "wizard.h"
#import "extended.h"
#import "bookmarks.h"
#import "interaction.h"
#import "embeddedwindow.h"
#import "update.h"
#import "AppleRemote.h"
55
#import "eyetv.h"
56
#import "simple_prefs.h"
57

58
#import <vlc_input.h>
59
#import <vlc_interface.h>
60

61 62 63
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
64
static void Run ( intf_thread_t *p_intf );
65

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
/* Quick hack */
/*****************************************************************************
 * VLCApplication implementation (this hack is really disgusting now,
 *                                feel free to fix.)
 *****************************************************************************/
@interface VLCApplication : NSApplication
{
   libvlc_int_t *o_libvlc;
}
- (void)setVLC: (libvlc_int_t *)p_libvlc;
@end


@implementation VLCApplication
- (void)setVLC: (libvlc_int_t *) p_libvlc
{
    o_libvlc = p_libvlc;
}
- (void)terminate: (id)sender
{
    vlc_object_kill( o_libvlc );
    [super terminate: sender];
}
@end

91 92 93
/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
94
int OpenIntf ( vlc_object_t *p_this )
Jérome Decoodt's avatar
Jérome Decoodt committed
95
{
96 97 98 99 100 101 102 103 104
    intf_thread_t *p_intf = (intf_thread_t*) p_this;

    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
    if( p_intf->p_sys == NULL )
    {
        return( 1 );
    }

    memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
Jérome Decoodt's avatar
Jérome Decoodt committed
105

106
    p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
107

108
    p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
109
    p_intf->pf_run = Run;
110
    p_intf->b_should_run_on_first_thread = true;
111 112 113 114 115 116 117

    return( 0 );
}

/*****************************************************************************
 * CloseIntf: destroy interface
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
118
void CloseIntf ( vlc_object_t *p_this )
119 120
{
    intf_thread_t *p_intf = (intf_thread_t*) p_this;
Jérome Decoodt's avatar
Jérome Decoodt committed
121

122 123
    [[VLCMain sharedInstance] setIntf: nil];
    
124
    msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
Jérome Decoodt's avatar
Jérome Decoodt committed
125

126
    [p_intf->p_sys->o_pool release];
Jérome Decoodt's avatar
Jérome Decoodt committed
127

128 129 130 131 132 133
    free( p_intf->p_sys );
}

/*****************************************************************************
 * Run: main loop
 *****************************************************************************/
134 135
jmp_buf jmpbuffer;

136 137
static void Run( intf_thread_t *p_intf )
{
138 139
    sigset_t set;

140 141 142 143
    /* Do it again - for some unknown reason, vlc_thread_create() often
     * fails to go to real-time priority with the first launched thread
     * (???) --Meuuh */
    vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
144 145 146 147 148 149 150 151 152

    /* Make sure the "force quit" menu item does quit instantly.
     * VLC overrides SIGTERM which is sent by the "force quit"
     * menu item to make sure deamon mode quits gracefully, so
     * we un-override SIGTERM here. */
    sigemptyset( &set );
    sigaddset( &set, SIGTERM );
    pthread_sigmask( SIG_UNBLOCK, &set, NULL );

153 154 155 156 157 158 159 160
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];

    /* Install a jmpbuffer to where we can go back before the NSApp exit
     * see applicationWillTerminate: */
    /* We need that code to run on main thread */
    [VLCApplication sharedApplication];
    [NSApp setVLC: p_intf->p_libvlc];

Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
161
    [[VLCMain sharedInstance] setIntf: p_intf];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
162
    [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
163 164 165 166 167

    /* Install a jmpbuffer to where we can go back before the NSApp exit
     * see applicationWillTerminate: */
    if(setjmp(jmpbuffer) == 0)
        [NSApp run];
168 169

    [o_pool release];
170 171
}

172 173 174 175 176 177 178 179 180 181 182 183
/*****************************************************************************
 * ManageThread: An ugly thread that polls
 *****************************************************************************/
static void * ManageThread( void *user_data )
{
    id self = user_data;

    [self manage];

    return NULL;
}

184 185 186 187
/*****************************************************************************
 * playlistChanged: Callback triggered by the intf-change playlist
 * variable, to let the intf update the playlist.
 *****************************************************************************/
188
static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
189 190
                     vlc_value_t old_val, vlc_value_t new_val, void *param )
{
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
191
    intf_thread_t * p_intf = VLCIntf;
192
    p_intf->p_sys->b_intf_update = true;
193
    p_intf->p_sys->b_playlist_update = true;
194 195
    p_intf->p_sys->b_playmode_update = true;
    p_intf->p_sys->b_current_title_update = true;
196 197 198 199 200 201 202 203 204 205 206 207
    return VLC_SUCCESS;
}

/*****************************************************************************
 * 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
 *****************************************************************************/
static int ShowController( vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param )
{
    intf_thread_t * p_intf = VLCIntf;
208
    p_intf->p_sys->b_intf_show = true;
209 210 211
    return VLC_SUCCESS;
}

212 213 214 215
/*****************************************************************************
 * FullscreenChanged: Callback triggered by the fullscreen-change playlist
 * variable, to let the intf update the controller.
 *****************************************************************************/
216
static int FullscreenChanged( vlc_object_t *p_this, const char *psz_variable,
217 218 219
                     vlc_value_t old_val, vlc_value_t new_val, void *param )
{
    intf_thread_t * p_intf = VLCIntf;
220
    p_intf->p_sys->b_fullscreen_update = true;
221 222 223
    return VLC_SUCCESS;
}

224 225 226 227 228 229 230 231 232 233 234
/*****************************************************************************
 * InteractCallback: Callback triggered by the interaction
 * variable, to let the intf display error and interaction dialogs
 *****************************************************************************/
static int InteractCallback( vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param )
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
    VLCMain *interface = (VLCMain *)param;
    interaction_dialog_t *p_dialog = (interaction_dialog_t *)(new_val.p_address);
    NSValue *o_value = [NSValue valueWithPointer:p_dialog];
235
 
236 237
    [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCNewInteractionEventNotification" object:[interface getInteractionList]
     userInfo:[NSDictionary dictionaryWithObject:o_value forKey:@"VLCDialogPointer"]];
238
 
239 240 241
    [o_pool release];
    return VLC_SUCCESS;
}
242

243 244 245
static struct
{
    unichar i_nskey;
246
    unsigned int i_vlckey;
Jérome Decoodt's avatar
Jérome Decoodt committed
247
} nskeys_to_vlckeys[] =
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
{
    { NSUpArrowFunctionKey, KEY_UP },
    { NSDownArrowFunctionKey, KEY_DOWN },
    { NSLeftArrowFunctionKey, KEY_LEFT },
    { NSRightArrowFunctionKey, KEY_RIGHT },
    { NSF1FunctionKey, KEY_F1 },
    { NSF2FunctionKey, KEY_F2 },
    { NSF3FunctionKey, KEY_F3 },
    { NSF4FunctionKey, KEY_F4 },
    { NSF5FunctionKey, KEY_F5 },
    { NSF6FunctionKey, KEY_F6 },
    { NSF7FunctionKey, KEY_F7 },
    { NSF8FunctionKey, KEY_F8 },
    { NSF9FunctionKey, KEY_F9 },
    { NSF10FunctionKey, KEY_F10 },
    { NSF11FunctionKey, KEY_F11 },
    { NSF12FunctionKey, KEY_F12 },
    { NSHomeFunctionKey, KEY_HOME },
    { NSEndFunctionKey, KEY_END },
    { NSPageUpFunctionKey, KEY_PAGEUP },
    { NSPageDownFunctionKey, KEY_PAGEDOWN },
    { NSTabCharacter, KEY_TAB },
    { NSCarriageReturnCharacter, KEY_ENTER },
    { NSEnterCharacter, KEY_ENTER },
    { NSBackspaceCharacter, KEY_BACKSPACE },
    { (unichar) ' ', KEY_SPACE },
    { (unichar) 0x1b, KEY_ESC },
    {0,0}
};

278
unichar VLCKeyToCocoa( unsigned int i_key )
279
{
280
    unsigned int i;
Jérome Decoodt's avatar
Jérome Decoodt committed
281

282 283 284 285 286 287 288 289 290 291 292 293 294
    for( i = 0; nskeys_to_vlckeys[i].i_vlckey != 0; i++ )
    {
        if( nskeys_to_vlckeys[i].i_vlckey == (i_key & ~KEY_MODIFIER) )
        {
            return nskeys_to_vlckeys[i].i_nskey;
        }
    }
    return (unichar)(i_key & ~KEY_MODIFIER);
}

unsigned int CocoaKeyToVLC( unichar i_key )
{
    unsigned int i;
Jérome Decoodt's avatar
Jérome Decoodt committed
295

296 297 298 299 300 301 302
    for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
    {
        if( nskeys_to_vlckeys[i].i_nskey == i_key )
        {
            return nskeys_to_vlckeys[i].i_vlckey;
        }
    }
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
    return (unsigned int)i_key;
}

unsigned int VLCModifiersToCocoa( unsigned int i_key )
{
    unsigned int new = 0;
    if( i_key & KEY_MODIFIER_COMMAND )
        new |= NSCommandKeyMask;
    if( i_key & KEY_MODIFIER_ALT )
        new |= NSAlternateKeyMask;
    if( i_key & KEY_MODIFIER_SHIFT )
        new |= NSShiftKeyMask;
    if( i_key & KEY_MODIFIER_CTRL )
        new |= NSControlKeyMask;
    return new;
318 319
}

320
/*****************************************************************************
Jérome Decoodt's avatar
Jérome Decoodt committed
321
 * VLCMain implementation
322 323 324
 *****************************************************************************/
@implementation VLCMain

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
325 326 327 328 329 330 331
static VLCMain *_o_sharedMainInstance = nil;

+ (VLCMain *)sharedInstance
{
    return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
}

Jérome Decoodt's avatar
Jérome Decoodt committed
332
- (id)init
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
333
{
334 335
    if( _o_sharedMainInstance) 
    {
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
336
        [self dealloc];
337 338 339
        return _o_sharedMainInstance;
    } 
    else
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
340
        _o_sharedMainInstance = [super init];
341

342
    o_about = [[VLAboutBox alloc] init];
343
    o_prefs = nil;
344
    o_open = [[VLCOpen alloc] init];
345
    o_wizard = [[VLCWizard alloc] init];
346
    o_extended = nil;
347
    o_bookmarks = [[VLCBookmarks alloc] init];
348
    o_embedded_list = [[VLCEmbeddedList alloc] init];
349
    o_interaction_list = [[VLCInteractionList alloc] init];
350
    o_info = [[VLCInfo alloc] init];
351
#ifdef UPDATE_CHECK
Rafaël Carré's avatar
Rafaël Carré committed
352
    o_update = [[VLCUpdate alloc] init];
353
#endif
354 355

    i_lastShownVolume = -1;
356 357

    o_remote = [[AppleRemote alloc] init];
358
    [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
359
    [o_remote setDelegate: _o_sharedMainInstance];
360

361 362 363 364 365 366 367 368
    o_eyetv = [[VLCEyeTVController alloc] init];

    /* announce our launch to a potential eyetv plugin */
    [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
                                                                   object: @"VLCEyeTVSupport"
                                                                 userInfo: NULL
                                                       deliverImmediately: YES];

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
369 370 371 372 373 374 375 376 377 378 379
    return _o_sharedMainInstance;
}

- (void)setIntf: (intf_thread_t *)p_mainintf {
    p_intf = p_mainintf;
}

- (intf_thread_t *)getIntf {
    return p_intf;
}

380 381
- (void)awakeFromNib
{
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
382
    unsigned int i_key = 0;
383
    playlist_t *p_playlist;
384
    vlc_value_t val;
385

386 387 388
    /* Check if we already did this once. Opening the other nibs calls it too, because VLCMain is the owner */
    if( nib_main_loaded ) return;

389
    [self initStrings];
390
    [o_window setExcludedFromWindowsMenu: TRUE];
391 392
    [o_msgs_panel setExcludedFromWindowsMenu: TRUE];
    [o_msgs_panel setDelegate: self];
Jérome Decoodt's avatar
Jérome Decoodt committed
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
    i_key = config_GetInt( p_intf, "key-quit" );
    [o_mi_quit setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_quit setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-play-pause" );
    [o_mi_play setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_play setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-stop" );
    [o_mi_stop setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_stop setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-faster" );
    [o_mi_faster setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_faster setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-slower" );
    [o_mi_slower setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_slower setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-prev" );
    [o_mi_previous setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_previous setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-next" );
    [o_mi_next setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_next setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
415
    i_key = config_GetInt( p_intf, "key-jump+short" );
416 417
    [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
418
    i_key = config_GetInt( p_intf, "key-jump-short" );
419 420
    [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
421
    i_key = config_GetInt( p_intf, "key-jump+medium" );
422 423
    [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
424
    i_key = config_GetInt( p_intf, "key-jump-medium" );
425 426
    [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
427
    i_key = config_GetInt( p_intf, "key-jump+long" );
428 429
    [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
430
    i_key = config_GetInt( p_intf, "key-jump-long" );
431 432
    [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
433 434 435 436 437 438
    i_key = config_GetInt( p_intf, "key-vol-up" );
    [o_mi_vol_up setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_vol_up setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
    i_key = config_GetInt( p_intf, "key-vol-down" );
    [o_mi_vol_down setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_vol_down setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
439 440 441
    i_key = config_GetInt( p_intf, "key-vol-mute" );
    [o_mi_mute setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_mute setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
442 443 444
    i_key = config_GetInt( p_intf, "key-fullscreen" );
    [o_mi_fullscreen setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fullscreen setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
445 446 447
    i_key = config_GetInt( p_intf, "key-snapshot" );
    [o_mi_snapshot setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_snapshot setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
448

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

451 452
    [self setSubmenusEnabled: FALSE];
    [self manageVolumeSlider];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
453
    [o_window setDelegate: self];
454
 
455
    b_restore_size = false;
456 457 458
    if( [o_window frame].size.height <= 200 )
    {
        b_small_window = YES;
459 460 461
        [o_window setFrame: NSMakeRect( [o_window frame].origin.x,
            [o_window frame].origin.y, [o_window frame].size.width,
            [o_window minSize].height ) display: YES animate:YES];
462 463 464 465 466
        [o_playlist_view setAutoresizesSubviews: NO];
    }
    else
    {
        b_small_window = NO;
467
        [o_playlist_view setFrame: NSMakeRect( 0, 0, [o_window frame].size.width, [o_window frame].size.height - 95 )];
468 469 470 471 472
        [o_playlist_view setNeedsDisplay:YES];
        [o_playlist_view setAutoresizesSubviews: YES];
        [[o_window contentView] addSubview: o_playlist_view];
    }
    [self updateTogglePlaylistState];
Jérome Decoodt's avatar
Jérome Decoodt committed
473

474 475
    o_size_with_playlist = [o_window frame].size;

476
    p_playlist = pl_Yield( p_intf );
477

478
    var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
479
    val.b_bool = false;
480

481
    var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
482
    var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
483

484
    pl_Release( p_intf );
485
 
486 487
    var_Create( p_intf, "interaction", VLC_VAR_ADDRESS );
    var_AddCallback( p_intf, "interaction", InteractCallback, self );
488
    p_intf->b_interaction = true;
489

490
    /* update the playmode stuff */
491
    p_intf->p_sys->b_playmode_update = true;
492

493 494 495 496
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(refreshVoutDeviceMenu:)
                                                 name: NSApplicationDidChangeScreenParametersNotification
                                               object: nil];
497 498 499 500 501 502 503 504 505 506 507

    o_img_play = [NSImage imageNamed: @"play"];
    o_img_pause = [NSImage imageNamed: @"pause"];    
    
    [self controlTintChanged];

    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector( controlTintChanged )
                                                 name: NSControlTintDidChangeNotification
                                               object: nil];
    
508
    nib_main_loaded = TRUE;
509 510
}

511 512 513 514
- (void)controlTintChanged
{
    BOOL b_playing = NO;
    
515
    if( [o_btn_play alternateImage] == o_img_play_pressed )
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
        b_playing = YES;
    
    if( [NSColor currentControlTint] == NSGraphiteControlTint )
    {
        o_img_play_pressed = [NSImage imageNamed: @"play_graphite"];
        o_img_pause_pressed = [NSImage imageNamed: @"pause_graphite"];
        
        [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_graphite"]];
        [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_graphite"]];
        [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_graphite"]];
        [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_graphite"]];
        [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_graphite"]];
        [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_graphite"]];
        [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_graphite"]];
        [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_graphite"]];
    }
    else
    {
        o_img_play_pressed = [NSImage imageNamed: @"play_blue"];
        o_img_pause_pressed = [NSImage imageNamed: @"pause_blue"];
        
        [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_blue"]];
        [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_blue"]];
        [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_blue"]];
        [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_blue"]];
        [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_blue"]];
        [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_blue"]];
        [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_blue"]];
        [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_blue"]];
    }
    
    if( b_playing )
548
        [o_btn_play setAlternateImage: o_img_play_pressed];
549
    else
550
        [o_btn_play setAlternateImage: o_img_pause_pressed];
551 552
}

553
- (void)initStrings
554 555
{
    [o_window setTitle: _NS("VLC - Controller")];
556
    [self setScrollField:_NS("VLC media player") stopAfter:-1];
557

Jon Lech Johansen's avatar
Jon Lech Johansen committed
558 559
    /* button controls */
    [o_btn_prev setToolTip: _NS("Previous")];
560
    [o_btn_rewind setToolTip: _NS("Rewind")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
561 562
    [o_btn_play setToolTip: _NS("Play")];
    [o_btn_stop setToolTip: _NS("Stop")];
563
    [o_btn_ff setToolTip: _NS("Fast Forward")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
564
    [o_btn_next setToolTip: _NS("Next")];
565
    [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
566 567
    [o_volumeslider setToolTip: _NS("Volume")];
    [o_timeslider setToolTip: _NS("Position")];
568
    [o_btn_playlist setToolTip: _NS("Playlist")];
569

Jérome Decoodt's avatar
Jérome Decoodt committed
570
    /* messages panel */
571
    [o_msgs_panel setTitle: _NS("Messages")];
572
    [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog...")];
573

Jon Lech Johansen's avatar
Jon Lech Johansen committed
574
    /* main menu */
575 576
    [o_mi_about setTitle: [_NS("About VLC media player") \
        stringByAppendingString: @"..."]];
577
    [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
578
    [o_mi_prefs setTitle: _NS("Preferences...")];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
579 580
    [o_mi_add_intf setTitle: _NS("Add Interface")];
    [o_mu_add_intf setTitle: _NS("Add Interface")];
581
    [o_mi_services setTitle: _NS("Services")];
582
    [o_mi_hide setTitle: _NS("Hide VLC")];
583 584
    [o_mi_hide_others setTitle: _NS("Hide Others")];
    [o_mi_show_all setTitle: _NS("Show All")];
585
    [o_mi_quit setTitle: _NS("Quit VLC")];
586

Derk-Jan Hartman's avatar
ALL:  
Derk-Jan Hartman committed
587
    [o_mu_file setTitle: _ANS("1:File")];
588 589
    [o_mi_open_generic setTitle: _NS("Open File...")];
    [o_mi_open_file setTitle: _NS("Quick Open File...")];
590 591
    [o_mi_open_disc setTitle: _NS("Open Disc...")];
    [o_mi_open_net setTitle: _NS("Open Network...")];
592
    [o_mi_open_capture setTitle: _NS("Open Capture Device...")];
593 594
    [o_mi_open_recent setTitle: _NS("Open Recent")];
    [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
595
    [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
596 597 598 599 600 601 602 603

    [o_mu_edit setTitle: _NS("Edit")];
    [o_mi_cut setTitle: _NS("Cut")];
    [o_mi_copy setTitle: _NS("Copy")];
    [o_mi_paste setTitle: _NS("Paste")];
    [o_mi_clear setTitle: _NS("Clear")];
    [o_mi_select_all setTitle: _NS("Select All")];

604
    [o_mu_controls setTitle: _NS("Playback")];
605
    [o_mi_play setTitle: _NS("Play")];
606 607 608
    [o_mi_stop setTitle: _NS("Stop")];
    [o_mi_faster setTitle: _NS("Faster")];
    [o_mi_slower setTitle: _NS("Slower")];
Christophe Massiot's avatar
Christophe Massiot committed
609
    [o_mi_previous setTitle: _NS("Previous")];
610
    [o_mi_next setTitle: _NS("Next")];
611 612 613
    [o_mi_random setTitle: _NS("Random")];
    [o_mi_repeat setTitle: _NS("Repeat One")];
    [o_mi_loop setTitle: _NS("Repeat All")];
614 615
    [o_mi_fwd setTitle: _NS("Step Forward")];
    [o_mi_bwd setTitle: _NS("Step Backward")];
616

617
    [o_mi_program setTitle: _NS("Program")];
618
    [o_mu_program setTitle: _NS("Program")];
619
    [o_mi_title setTitle: _NS("Title")];
620
    [o_mu_title setTitle: _NS("Title")];
621
    [o_mi_chapter setTitle: _NS("Chapter")];
622
    [o_mu_chapter setTitle: _NS("Chapter")];
Jérome Decoodt's avatar
Jérome Decoodt committed
623

624
    [o_mu_audio setTitle: _NS("Audio")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
625 626
    [o_mi_vol_up setTitle: _NS("Volume Up")];
    [o_mi_vol_down setTitle: _NS("Volume Down")];
627
    [o_mi_mute setTitle: _NS("Mute")];
628 629 630 631 632 633
    [o_mi_audiotrack setTitle: _NS("Audio Track")];
    [o_mu_audiotrack setTitle: _NS("Audio Track")];
    [o_mi_channels setTitle: _NS("Audio Channels")];
    [o_mu_channels setTitle: _NS("Audio Channels")];
    [o_mi_device setTitle: _NS("Audio Device")];
    [o_mu_device setTitle: _NS("Audio Device")];
634 635
    [o_mi_visual setTitle: _NS("Visualizations")];
    [o_mu_visual setTitle: _NS("Visualizations")];
Jérome Decoodt's avatar
Jérome Decoodt committed
636

637
    [o_mu_video setTitle: _NS("Video")];
638 639 640
    [o_mi_half_window setTitle: _NS("Half Size")];
    [o_mi_normal_window setTitle: _NS("Normal Size")];
    [o_mi_double_window setTitle: _NS("Double Size")];
641
    [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
642
    [o_mi_fullscreen setTitle: _NS("Fullscreen")];
643
    [o_mi_floatontop setTitle: _NS("Float on Top")];
644
    [o_mi_snapshot setTitle: _NS("Snapshot")];
645 646
    [o_mi_videotrack setTitle: _NS("Video Track")];
    [o_mu_videotrack setTitle: _NS("Video Track")];
647 648
    [o_mi_aspect_ratio setTitle: _NS("Aspect-ratio")];
    [o_mu_aspect_ratio setTitle: _NS("Aspect-ratio")];
649 650
    [o_mi_crop setTitle: _NS("Crop")];
    [o_mu_crop setTitle: _NS("Crop")];
651 652
    [o_mi_screen setTitle: _NS("Fullscreen Video Device")];
    [o_mu_screen setTitle: _NS("Fullscreen Video Device")];
653 654
    [o_mi_subtitle setTitle: _NS("Subtitles Track")];
    [o_mu_subtitle setTitle: _NS("Subtitles Track")];
655
    [o_mi_deinterlace setTitle: _NS("Deinterlace")];
656
    [o_mu_deinterlace setTitle: _NS("Deinterlace")];
Benjamin Pracht's avatar
Benjamin Pracht committed
657 658
    [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
    [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
659 660

    [o_mu_window setTitle: _NS("Window")];
661 662
    [o_mi_minimize setTitle: _NS("Minimize Window")];
    [o_mi_close_window setTitle: _NS("Close Window")];
663 664 665 666 667 668 669 670
    [o_mi_controller setTitle: _NS("Controller...")];
    [o_mi_equalizer setTitle: _NS("Equalizer...")];
    [o_mi_extended setTitle: _NS("Extended Controls...")];
    [o_mi_bookmarks setTitle: _NS("Bookmarks...")];
    [o_mi_playlist setTitle: _NS("Playlist...")];
    [o_mi_info setTitle: _NS("Media Information...")];
    [o_mi_messages setTitle: _NS("Messages...")];
    [o_mi_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
671

672 673
    [o_mi_bring_atf setTitle: _NS("Bring All to Front")];

674
    [o_mu_help setTitle: _NS("Help")];
675
    [o_mi_help setTitle: _NS("VLC media player Help...")];
676
    [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
677
    [o_mi_license setTitle: _NS("License")];
678 679 680 681
    [o_mi_documentation setTitle: _NS("Online Documentation...")];
    [o_mi_website setTitle: _NS("VideoLAN Website...")];
    [o_mi_donation setTitle: _NS("Make a donation...")];
    [o_mi_forum setTitle: _NS("Online Forum...")];
682

683
    /* dock menu */
684
    [o_dmi_play setTitle: _NS("Play")];
685
    [o_dmi_stop setTitle: _NS("Stop")];
686 687
    [o_dmi_next setTitle: _NS("Next")];
    [o_dmi_previous setTitle: _NS("Previous")];
688
    [o_dmi_mute setTitle: _NS("Mute")];
689
 
690 691 692 693 694 695 696 697 698 699
    /* vout menu */
    [o_vmi_play setTitle: _NS("Play")];
    [o_vmi_stop setTitle: _NS("Stop")];
    [o_vmi_prev setTitle: _NS("Previous")];
    [o_vmi_next setTitle: _NS("Next")];
    [o_vmi_volup setTitle: _NS("Volume Up")];
    [o_vmi_voldown setTitle: _NS("Volume Down")];
    [o_vmi_mute setTitle: _NS("Mute")];
    [o_vmi_fullscreen setTitle: _NS("Fullscreen")];
    [o_vmi_snapshot setTitle: _NS("Snapshot")];
700 701 702 703
}

- (void)applicationWillFinishLaunching:(NSNotification *)o_notification
{
704 705 706
    o_msg_lock = [[NSLock alloc] init];
    o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];

707
    /* FIXME: don't poll */
708
    interfaceTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
709
                                     target: self selector: @selector(manageIntf:)
710
                                   userInfo: nil repeats: FALSE] retain];
711

712 713
    /* Note: we use the pthread API to support pre-10.5 */
    pthread_create( &manage_thread, NULL, ManageThread, self );
Jérome Decoodt's avatar
Jérome Decoodt committed
714

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
715 716
    [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
        var: "intf-add" selector: @selector(toggleVar:)];
717

718
    /* check whether the user runs a valid version of OSX; alert is auto-released */
719
    if( MACOS_VERSION < 10.4f )
720 721 722 723 724 725 726 727 728 729 730 731 732
    {
        NSAlert *ourAlert;
        int i_returnValue;
        ourAlert = [NSAlert alertWithMessageText: _NS("Your version of Mac OS X is not supported")
                        defaultButton: _NS("Quit")
                      alternateButton: NULL
                          otherButton: NULL
            informativeTextWithFormat: _NS("VLC media player requires Mac OS X 10.4 or higher.")];
        [ourAlert setAlertStyle: NSCriticalAlertStyle];
        i_returnValue = [ourAlert runModal];
        [NSApp terminate: self];
    }

733
    vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
734
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
735

736 737
- (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
{
738
    BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
739
    NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
740 741 742 743
    if( b_autoplay )
        [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
    else
        [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: YES];
Jérome Decoodt's avatar
Jérome Decoodt committed
744

745 746
    return( TRUE );
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
747

748
- (NSString *)localizedString:(const char *)psz
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
749 750 751 752 753 754
{
    NSString * o_str = nil;

    if( psz != NULL )
    {
        o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
755

756
        if( o_str == NULL )
757 758 759 760
        {
            msg_Err( VLCIntf, "could not translate: %s", psz );
            return( @"" );
        }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
761
    }
762
    else
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
763
    {
764 765
        msg_Warn( VLCIntf, "can't translate empty strings" );
        return( @"" );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
766 767 768 769 770
    }

    return( o_str );
}

771 772
/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
773
{    
774
    if(!hasVisibleWindows)
775 776 777 778 779
        [o_window makeKeyAndOrderFront:self];

    return YES;
}

780 781
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
782
#ifdef UPDATE_CHECK
783
    /* Check for update silently on startup */
784
    if( !nib_update_loaded )
785
        nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self];
786

Rafaël Carré's avatar
Rafaël Carré committed
787 788
    if([o_update shouldCheckForUpdate])
        [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:NULL];
789 790
#endif

791 792 793
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];
794 795
}

796 797 798 799 800 801 802 803 804 805 806
/* Listen to the remote in exclusive mode, only when VLC is the active
   application */
- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
    [o_remote startListening: self];
}
- (void)applicationDidResignActive:(NSNotification *)aNotification
{
    [o_remote stopListening: self];
}

807 808 809 810
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
    /* Pause */
811
    if( p_intf->p_sys->i_play_status == PLAYING_S )
812 813 814 815 816 817 818
    {
        vlc_value_t val;
        val.i_int = config_GetInt( p_intf, "key-play-pause" );
        var_Set( p_intf->p_libvlc, "key-pressed", val );
    }
}

819 820
/* Helper method for the remote control interface in order to trigger forward/backward and volume
   increase/decrease as long as the user holds the left/right, plus/minus button */
821
- (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
822
{
823
    if(b_remote_button_hold)
824
    {
825
        switch([buttonIdentifierNumber intValue])
826
        {
827
            case kRemoteButtonRight_Hold:
828
                  [o_controls forward: self];
829 830
            break;
            case kRemoteButtonLeft_Hold:
831 832 833 834 835 836 837
                  [o_controls backward: self];
            break;
            case kRemoteButtonVolume_Plus_Hold:
                [o_controls volumeUp: self];
            break;
            case kRemoteButtonVolume_Minus_Hold:
                [o_controls volumeDown: self];
838
            break;
839
        }
840
        if(b_remote_button_hold)
841
        {
842
            /* trigger event */
843
            [self performSelector:@selector(executeHoldActionForRemoteButton:)
844
                         withObject:buttonIdentifierNumber
845
                         afterDelay:0.25];
846 847 848 849
        }
    }
}

850
/* Apple Remote callback */
851 852 853
- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
               pressedDown: (BOOL) pressedDown
                clickCount: (unsigned int) count
854 855 856 857
{
    switch( buttonIdentifier )
    {
        case kRemoteButtonPlay:
858
            if(count >= 2) {
859 860 861
                [o_controls toogleFullscreen:self];
            } else {
                [o_controls play: self];
862
            }
863
            break;
Eric Petit's avatar
Eric Petit committed
864
        case kRemoteButtonVolume_Plus:
865
            [o_controls volumeUp: self];
Eric Petit's avatar
Eric Petit committed
866 867
            break;
        case kRemoteButtonVolume_Minus:
868
            [o_controls volumeDown: self];
Eric Petit's avatar
Eric Petit committed
869
            break;
870 871 872 873 874 875 876 877
        case kRemoteButtonRight:
            [o_controls next: self];
            break;
        case kRemoteButtonLeft:
            [o_controls prev: self];
            break;
        case kRemoteButtonRight_Hold:
        case kRemoteButtonLeft_Hold:
878 879
        case kRemoteButtonVolume_Plus_Hold:
        case kRemoteButtonVolume_Minus_Hold:
880
            /* simulate an event as long as the user holds the button */
881
            b_remote_button_hold = pressedDown;
882
            if( pressedDown )
883 884 885
            {
                NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];
                [self performSelector:@selector(executeHoldActionForRemoteButton:)
886 887
                           withObject:buttonIdentifierNumber];
            }
888 889
            break;
        case kRemoteButtonMenu:
890
            [o_controls showPosition: self];
891
            break;
892 893 894 895 896 897
        default:
            /* Add here whatever you want other buttons to do */
            break;
    }
}

Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
898 899 900 901 902 903
- (char *)delocalizeString:(NSString *)id
{
    NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
                          allowLossyConversion: NO];
    char * psz_string;

904
    if( o_data == nil )
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
905 906 907
    {
        o_data = [id dataUsingEncoding: NSUTF8StringEncoding
                     allowLossyConversion: YES];
Jérome Decoodt's avatar
Jérome Decoodt committed
908
        psz_string = malloc( [o_data length] + 1 );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
909 910
        [o_data getBytes: psz_string];
        psz_string[ [o_data length] ] = '\0';
911
        msg_Err( VLCIntf