intf.m 80.8 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 391 392

    [o_window setExcludedFromWindowsMenu: YES];
    [o_msgs_panel setExcludedFromWindowsMenu: YES];
393
    [o_msgs_panel setDelegate: self];
Jérome Decoodt's avatar
Jérome Decoodt committed
394

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    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)];
416
    i_key = config_GetInt( p_intf, "key-jump+short" );
417 418
    [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
419
    i_key = config_GetInt( p_intf, "key-jump-short" );
420 421
    [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
422
    i_key = config_GetInt( p_intf, "key-jump+medium" );
423 424
    [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
425
    i_key = config_GetInt( p_intf, "key-jump-medium" );
426 427
    [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
428
    i_key = config_GetInt( p_intf, "key-jump+long" );
429 430
    [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
431
    i_key = config_GetInt( p_intf, "key-jump-long" );
432 433
    [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
    [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
434 435 436 437 438 439
    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
440 441 442
    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)];
443 444 445
    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)];
446 447 448
    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)];
449

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

452
    [self setSubmenusEnabled: FALSE];
453
    [o_volumeslider setEnabled: YES];
454
    [self manageVolumeSlider];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
455
    [o_window setDelegate: self];
456
 
457
    b_restore_size = false;
458 459 460 461 462

    // Set that here as IB seems to be buggy
    [o_window setContentMinSize:NSMakeSize(338., 30.)];

    if( [o_window contentRectForFrameRect:[o_window frame]].size.height <= 169. )
463 464
    {
        b_small_window = YES;
465 466 467
        [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];
468 469 470 471 472
        [o_playlist_view setAutoresizesSubviews: NO];
    }
    else
    {
        b_small_window = NO;
473 474
        NSRect contentRect = [o_window contentRectForFrameRect:[o_window frame]];
        [o_playlist_view setFrame: NSMakeRect( 0, 0, contentRect.size.width, contentRect.size.height - [o_window contentMinSize].height )];
475 476 477 478
        [o_playlist_view setNeedsDisplay:YES];
        [o_playlist_view setAutoresizesSubviews: YES];
        [[o_window contentView] addSubview: o_playlist_view];
    }
479

480
    [self updateTogglePlaylistState];
Jérome Decoodt's avatar
Jérome Decoodt committed
481

482
    o_size_with_playlist = [o_window contentRectForFrameRect:[o_window frame]].size;
483

484
    p_playlist = pl_Yield( p_intf );
485

486
    var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
487
    val.b_bool = false;
488

489
    var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
490
    var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
491

492
    pl_Release( p_intf );
493
 
494 495
    var_Create( p_intf, "interaction", VLC_VAR_ADDRESS );
    var_AddCallback( p_intf, "interaction", InteractCallback, self );
496
    p_intf->b_interaction = true;
497

498
    /* update the playmode stuff */
499
    p_intf->p_sys->b_playmode_update = true;
500

501 502 503 504
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(refreshVoutDeviceMenu:)
                                                 name: NSApplicationDidChangeScreenParametersNotification
                                               object: nil];
505 506 507 508 509 510 511 512 513 514 515

    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];
    
516
    nib_main_loaded = TRUE;
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 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
#pragma mark Toolbar delegate
/* Our item identifiers */
static NSString * VLCToolbarMediaControl     = @"VLCToolbarMediaControl";

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
{
    return [NSArray arrayWithObjects:
//                        NSToolbarCustomizeToolbarItemIdentifier,
//                        NSToolbarFlexibleSpaceItemIdentifier,
//                        NSToolbarSpaceItemIdentifier,
//                        NSToolbarSeparatorItemIdentifier,
                        VLCToolbarMediaControl,
                        nil ];
}

- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
{
    return [NSArray arrayWithObjects:
                        VLCToolbarMediaControl,
                        nil ];
}

- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
    NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];

 
    if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
    {
        [toolbarItem setLabel:@"Media Controls"];
        [toolbarItem setPaletteLabel:@"Media Controls"];

        NSSize size = toolbarMediaControl.frame.size;
        [toolbarItem setView:toolbarMediaControl];
        [toolbarItem setMinSize:size];
        size.width += 1000.;
        [toolbarItem setMaxSize:size];

        // Hack: For some reason we need to make sure
        // that the those element are on top
        // Add them again will put them frontmost
        [toolbarMediaControl addSubview:o_scrollfield];
        [toolbarMediaControl addSubview:o_timeslider];
        [toolbarMediaControl addSubview:o_timefield];
        [toolbarMediaControl addSubview:o_main_pgbar];

        /* TODO: setup a menu */
    }
    else
    {
        /* itemIdentifier referred to a toolbar item that is not
         * provided or supported by us or Cocoa
         * Returning nil will inform the toolbar
         * that this kind of item is not supported */
        toolbarItem = nil;
    }
    return toolbarItem;
}

#pragma mark -

580 581 582 583
- (void)controlTintChanged
{
    BOOL b_playing = NO;
    
584
    if( [o_btn_play alternateImage] == o_img_play_pressed )
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
        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 )
617
        [o_btn_play setAlternateImage: o_img_play_pressed];
618
    else
619
        [o_btn_play setAlternateImage: o_img_pause_pressed];
620 621
}

622
- (void)initStrings
623
{
624
    [o_window setTitle: _NS("VLC")];
625
    [self setScrollField:_NS("VLC media player") stopAfter:-1];
626

Jon Lech Johansen's avatar
Jon Lech Johansen committed
627 628
    /* button controls */
    [o_btn_prev setToolTip: _NS("Previous")];
629
    [o_btn_rewind setToolTip: _NS("Rewind")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
630 631
    [o_btn_play setToolTip: _NS("Play")];
    [o_btn_stop setToolTip: _NS("Stop")];
632
    [o_btn_ff setToolTip: _NS("Fast Forward")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
633
    [o_btn_next setToolTip: _NS("Next")];
634
    [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
635 636
    [o_volumeslider setToolTip: _NS("Volume")];
    [o_timeslider setToolTip: _NS("Position")];
637
    [o_btn_playlist setToolTip: _NS("Playlist")];
638

Jérome Decoodt's avatar
Jérome Decoodt committed
639
    /* messages panel */
640
    [o_msgs_panel setTitle: _NS("Messages")];
641
    [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog...")];
642

Jon Lech Johansen's avatar
Jon Lech Johansen committed
643
    /* main menu */
644 645
    [o_mi_about setTitle: [_NS("About VLC media player") \
        stringByAppendingString: @"..."]];
646
    [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
647
    [o_mi_prefs setTitle: _NS("Preferences...")];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
648 649
    [o_mi_add_intf setTitle: _NS("Add Interface")];
    [o_mu_add_intf setTitle: _NS("Add Interface")];
650
    [o_mi_services setTitle: _NS("Services")];
651
    [o_mi_hide setTitle: _NS("Hide VLC")];
652 653
    [o_mi_hide_others setTitle: _NS("Hide Others")];
    [o_mi_show_all setTitle: _NS("Show All")];
654
    [o_mi_quit setTitle: _NS("Quit VLC")];
655

Derk-Jan Hartman's avatar
ALL:  
Derk-Jan Hartman committed
656
    [o_mu_file setTitle: _ANS("1:File")];
657 658
    [o_mi_open_generic setTitle: _NS("Open File...")];
    [o_mi_open_file setTitle: _NS("Quick Open File...")];
659 660
    [o_mi_open_disc setTitle: _NS("Open Disc...")];
    [o_mi_open_net setTitle: _NS("Open Network...")];
661
    [o_mi_open_capture setTitle: _NS("Open Capture Device...")];
662 663
    [o_mi_open_recent setTitle: _NS("Open Recent")];
    [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
664
    [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
665 666 667 668 669 670 671 672

    [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")];

673
    [o_mu_controls setTitle: _NS("Playback")];
674
    [o_mi_play setTitle: _NS("Play")];
675 676 677
    [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
678
    [o_mi_previous setTitle: _NS("Previous")];
679
    [o_mi_next setTitle: _NS("Next")];
680 681 682
    [o_mi_random setTitle: _NS("Random")];
    [o_mi_repeat setTitle: _NS("Repeat One")];
    [o_mi_loop setTitle: _NS("Repeat All")];
683 684
    [o_mi_fwd setTitle: _NS("Step Forward")];
    [o_mi_bwd setTitle: _NS("Step Backward")];
685

686
    [o_mi_program setTitle: _NS("Program")];
687
    [o_mu_program setTitle: _NS("Program")];
688
    [o_mi_title setTitle: _NS("Title")];
689
    [o_mu_title setTitle: _NS("Title")];
690
    [o_mi_chapter setTitle: _NS("Chapter")];
691
    [o_mu_chapter setTitle: _NS("Chapter")];
Jérome Decoodt's avatar
Jérome Decoodt committed
692

693
    [o_mu_audio setTitle: _NS("Audio")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
694 695
    [o_mi_vol_up setTitle: _NS("Volume Up")];
    [o_mi_vol_down setTitle: _NS("Volume Down")];
696
    [o_mi_mute setTitle: _NS("Mute")];
697 698 699 700 701 702
    [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")];
703 704
    [o_mi_visual setTitle: _NS("Visualizations")];
    [o_mu_visual setTitle: _NS("Visualizations")];
Jérome Decoodt's avatar
Jérome Decoodt committed
705

706
    [o_mu_video setTitle: _NS("Video")];
707 708 709
    [o_mi_half_window setTitle: _NS("Half Size")];
    [o_mi_normal_window setTitle: _NS("Normal Size")];
    [o_mi_double_window setTitle: _NS("Double Size")];
710
    [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
711
    [o_mi_fullscreen setTitle: _NS("Fullscreen")];
712
    [o_mi_floatontop setTitle: _NS("Float on Top")];
713
    [o_mi_snapshot setTitle: _NS("Snapshot")];
714 715
    [o_mi_videotrack setTitle: _NS("Video Track")];
    [o_mu_videotrack setTitle: _NS("Video Track")];
716 717
    [o_mi_aspect_ratio setTitle: _NS("Aspect-ratio")];
    [o_mu_aspect_ratio setTitle: _NS("Aspect-ratio")];
718 719
    [o_mi_crop setTitle: _NS("Crop")];
    [o_mu_crop setTitle: _NS("Crop")];
720 721
    [o_mi_screen setTitle: _NS("Fullscreen Video Device")];
    [o_mu_screen setTitle: _NS("Fullscreen Video Device")];
722 723
    [o_mi_subtitle setTitle: _NS("Subtitles Track")];
    [o_mu_subtitle setTitle: _NS("Subtitles Track")];
724
    [o_mi_deinterlace setTitle: _NS("Deinterlace")];
725
    [o_mu_deinterlace setTitle: _NS("Deinterlace")];
Benjamin Pracht's avatar
Benjamin Pracht committed
726 727
    [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
    [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
728 729

    [o_mu_window setTitle: _NS("Window")];
730 731
    [o_mi_minimize setTitle: _NS("Minimize Window")];
    [o_mi_close_window setTitle: _NS("Close Window")];
732 733 734 735 736 737 738 739
    [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...")];
740

741 742
    [o_mi_bring_atf setTitle: _NS("Bring All to Front")];

743
    [o_mu_help setTitle: _NS("Help")];
744
    [o_mi_help setTitle: _NS("VLC media player Help...")];
745
    [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
746
    [o_mi_license setTitle: _NS("License")];
747 748 749 750
    [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...")];
751

752
    /* dock menu */
753
    [o_dmi_play setTitle: _NS("Play")];
754
    [o_dmi_stop setTitle: _NS("Stop")];
755 756
    [o_dmi_next setTitle: _NS("Next")];
    [o_dmi_previous setTitle: _NS("Previous")];
757
    [o_dmi_mute setTitle: _NS("Mute")];
758
 
759 760 761 762 763 764 765 766 767 768
    /* 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")];
769 770 771 772
}

- (void)applicationWillFinishLaunching:(NSNotification *)o_notification
{
773 774 775
    o_msg_lock = [[NSLock alloc] init];
    o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];

776
    /* FIXME: don't poll */
777
    interfaceTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
778
                                     target: self selector: @selector(manageIntf:)
779
                                   userInfo: nil repeats: FALSE] retain];
780

781 782
    /* 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
783

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

787
    /* check whether the user runs a valid version of OSX; alert is auto-released */
788
    if( MACOS_VERSION < 10.4f )
789 790 791 792 793 794 795 796 797 798 799 800 801
    {
        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];
    }

802
    vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
803
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
804

805 806
- (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
{
807
    BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
808
    NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
809 810 811 812
    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
813

814 815
    return( TRUE );
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
816

817
- (NSString *)localizedString:(const char *)psz
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
818 819 820 821 822 823
{
    NSString * o_str = nil;

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

825
        if( o_str == NULL )
826 827 828 829
        {
            msg_Err( VLCIntf, "could not translate: %s", psz );
            return( @"" );
        }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
830
    }
831
    else
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
832
    {
833 834
        msg_Warn( VLCIntf, "can't translate empty strings" );
        return( @"" );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
835 836 837 838 839
    }

    return( o_str );
}

840 841
/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
842
{    
843
    if(!hasVisibleWindows)
844 845 846 847 848
        [o_window makeKeyAndOrderFront:self];

    return YES;
}

849 850
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
851
#ifdef UPDATE_CHECK
852
    /* Check for update silently on startup */
853
    if( !nib_update_loaded )
854
        nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self];
855

Rafaël Carré's avatar
Rafaël Carré committed
856
    if([o_update shouldCheckForUpdate])
857
        [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:nil];
858 859
#endif

860 861 862
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];
863

864
    [NSThread detachNewThreadSelector:@selector(lookForCrashLog) toTarget:self withObject:nil];
865 866
}

867 868 869 870 871 872 873 874 875 876 877
/* 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];
}

878 879 880 881
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
    /* Pause */
882
    if( p_intf->p_sys->i_play_status == PLAYING_S )
883 884 885 886 887 888 889
    {
        vlc_value_t val;
        val.i_int = config_GetInt( p_intf, "key-play-pause" );
        var_Set( p_intf->p_libvlc, "key-pressed", val );
    }
}

890 891
/* 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 */
892
- (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
893
{
894
    if(b_remote_button_hold)
895
    {
896
        switch([buttonIdentifierNumber intValue])
897
        {
898
            case kRemoteButtonRight_Hold:
899
                  [o_controls forward: self];
900 901
            break;
            case kRemoteButtonLeft_Hold:
902 903 904 905 906 907 908
                  [o_controls backward: self];
            break;
            case kRemoteButtonVolume_Plus_Hold:
                [o_controls volumeUp: self];
            break;
            case kRemoteButtonVolume_Minus_Hold:
                [o_controls volumeDown: self];
909
            break;
910
        }
911
        if(b_remote_button_hold)
912
        {
913
            /* trigger event */
914
            [self performSelector:@selector(executeHoldActionForRemoteButton:)
915
                         withObject:buttonIdentifierNumber
916
                         afterDelay:0.25];
917 918 919 920
        }
    }
}

921
/* Apple Remote callback */
922 923 924
- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
               pressedDown: (BOOL) pressedDown
                clickCount: (unsigned int) count
925 926 927 928
{
    switch( buttonIdentifier )
    {
        case kRemoteButtonPlay:
929
            if(count >= 2) {
930 931 932
                [o_controls toogleFullscreen:self];
            } else {
                [o_controls play: self];
933
            }
934
            break;
Eric Petit's avatar
Eric Petit committed
935
        case kRemoteButtonVolume_Plus:
936
            [o_controls volumeUp: self];
Eric Petit's avatar
Eric Petit committed
937 938
            break;
        case kRemoteButtonVolume_Minus:<