intf.m 81.4 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
    /* In code and not in Nib for 10.4 compat */
    NSToolbar * toolbar = [[[NSToolbar alloc] initWithIdentifier:@"mainControllerToolbar"] autorelease];
    [toolbar setDelegate:self];
    [toolbar setShowsBaselineSeparator:NO];
    [toolbar setAllowsUserCustomization:NO];
    [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
    [toolbar setAutosavesConfiguration:YES];
    [o_window setToolbar:toolbar];

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

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

461
    [self setSubmenusEnabled: FALSE];
462
    [o_volumeslider setEnabled: YES];
463
    [self manageVolumeSlider];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
464
    [o_window setDelegate: self];
465
 
466
    b_restore_size = false;
467 468 469 470 471

    // 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. )
472 473
    {
        b_small_window = YES;
474 475 476
        [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];
477 478 479 480 481
        [o_playlist_view setAutoresizesSubviews: NO];
    }
    else
    {
        b_small_window = NO;
482 483
        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 )];
484 485 486 487
        [o_playlist_view setNeedsDisplay:YES];
        [o_playlist_view setAutoresizesSubviews: YES];
        [[o_window contentView] addSubview: o_playlist_view];
    }
488

489
    [self updateTogglePlaylistState];
Jérome Decoodt's avatar
Jérome Decoodt committed
490

491
    o_size_with_playlist = [o_window contentRectForFrameRect:[o_window frame]].size;
492

493
    p_playlist = pl_Yield( p_intf );
494

495
    var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
496
    val.b_bool = false;
497

498
    var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
499
    var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
500

501
    pl_Release( p_intf );
502
 
503 504
    var_Create( p_intf, "interaction", VLC_VAR_ADDRESS );
    var_AddCallback( p_intf, "interaction", InteractCallback, self );
505
    p_intf->b_interaction = true;
506

507
    /* update the playmode stuff */
508
    p_intf->p_sys->b_playmode_update = true;
509

510 511 512 513
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(refreshVoutDeviceMenu:)
                                                 name: NSApplicationDidChangeScreenParametersNotification
                                               object: nil];
514 515 516 517 518 519 520 521 522 523 524

    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];
    
525
    nib_main_loaded = TRUE;
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 580 581 582 583 584 585 586 587 588
#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 -

589 590 591 592
- (void)controlTintChanged
{
    BOOL b_playing = NO;
    
593
    if( [o_btn_play alternateImage] == o_img_play_pressed )
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        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 )
626
        [o_btn_play setAlternateImage: o_img_play_pressed];
627
    else
628
        [o_btn_play setAlternateImage: o_img_pause_pressed];
629 630
}

631
- (void)initStrings
632
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
633
    [o_window setTitle: _NS("VLC media player")];
634
    [self setScrollField:_NS("VLC media player") stopAfter:-1];
635

Jon Lech Johansen's avatar
Jon Lech Johansen committed
636 637
    /* button controls */
    [o_btn_prev setToolTip: _NS("Previous")];
638
    [o_btn_rewind setToolTip: _NS("Rewind")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
639 640
    [o_btn_play setToolTip: _NS("Play")];
    [o_btn_stop setToolTip: _NS("Stop")];
641
    [o_btn_ff setToolTip: _NS("Fast Forward")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
642
    [o_btn_next setToolTip: _NS("Next")];
643
    [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
644 645
    [o_volumeslider setToolTip: _NS("Volume")];
    [o_timeslider setToolTip: _NS("Position")];
646
    [o_btn_playlist setToolTip: _NS("Playlist")];
647

Jérome Decoodt's avatar
Jérome Decoodt committed
648
    /* messages panel */
649
    [o_msgs_panel setTitle: _NS("Messages")];
650
    [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog...")];
651

Jon Lech Johansen's avatar
Jon Lech Johansen committed
652
    /* main menu */
653 654
    [o_mi_about setTitle: [_NS("About VLC media player") \
        stringByAppendingString: @"..."]];
655
    [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
656
    [o_mi_prefs setTitle: _NS("Preferences...")];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
657 658
    [o_mi_add_intf setTitle: _NS("Add Interface")];
    [o_mu_add_intf setTitle: _NS("Add Interface")];
659
    [o_mi_services setTitle: _NS("Services")];
660
    [o_mi_hide setTitle: _NS("Hide VLC")];
661 662
    [o_mi_hide_others setTitle: _NS("Hide Others")];
    [o_mi_show_all setTitle: _NS("Show All")];
663
    [o_mi_quit setTitle: _NS("Quit VLC")];
664

Derk-Jan Hartman's avatar
ALL:  
Derk-Jan Hartman committed
665
    [o_mu_file setTitle: _ANS("1:File")];
666 667
    [o_mi_open_generic setTitle: _NS("Open File...")];
    [o_mi_open_file setTitle: _NS("Quick Open File...")];
668 669
    [o_mi_open_disc setTitle: _NS("Open Disc...")];
    [o_mi_open_net setTitle: _NS("Open Network...")];
670
    [o_mi_open_capture setTitle: _NS("Open Capture Device...")];
671 672
    [o_mi_open_recent setTitle: _NS("Open Recent")];
    [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
673
    [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
674 675 676 677 678 679 680 681

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

682
    [o_mu_controls setTitle: _NS("Playback")];
683
    [o_mi_play setTitle: _NS("Play")];
684 685 686
    [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
687
    [o_mi_previous setTitle: _NS("Previous")];
688
    [o_mi_next setTitle: _NS("Next")];
689 690 691
    [o_mi_random setTitle: _NS("Random")];
    [o_mi_repeat setTitle: _NS("Repeat One")];
    [o_mi_loop setTitle: _NS("Repeat All")];
692 693
    [o_mi_fwd setTitle: _NS("Step Forward")];
    [o_mi_bwd setTitle: _NS("Step Backward")];
694

695
    [o_mi_program setTitle: _NS("Program")];
696
    [o_mu_program setTitle: _NS("Program")];
697
    [o_mi_title setTitle: _NS("Title")];
698
    [o_mu_title setTitle: _NS("Title")];
699
    [o_mi_chapter setTitle: _NS("Chapter")];
700
    [o_mu_chapter setTitle: _NS("Chapter")];
Jérome Decoodt's avatar
Jérome Decoodt committed
701

702
    [o_mu_audio setTitle: _NS("Audio")];
Jon Lech Johansen's avatar
Jon Lech Johansen committed
703 704
    [o_mi_vol_up setTitle: _NS("Volume Up")];
    [o_mi_vol_down setTitle: _NS("Volume Down")];
705
    [o_mi_mute setTitle: _NS("Mute")];
706 707 708 709 710 711
    [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")];
712 713
    [o_mi_visual setTitle: _NS("Visualizations")];
    [o_mu_visual setTitle: _NS("Visualizations")];
Jérome Decoodt's avatar
Jérome Decoodt committed
714

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

    [o_mu_window setTitle: _NS("Window")];
739 740
    [o_mi_minimize setTitle: _NS("Minimize Window")];
    [o_mi_close_window setTitle: _NS("Close Window")];
741 742 743 744 745 746 747 748
    [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...")];
749

750 751
    [o_mi_bring_atf setTitle: _NS("Bring All to Front")];

752
    [o_mu_help setTitle: _NS("Help")];
753
    [o_mi_help setTitle: _NS("VLC media player Help...")];
754
    [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
755
    [o_mi_license setTitle: _NS("License")];
756 757 758 759
    [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...")];
760

761
    /* dock menu */
762
    [o_dmi_play setTitle: _NS("Play")];
763
    [o_dmi_stop setTitle: _NS("Stop")];
764 765
    [o_dmi_next setTitle: _NS("Next")];
    [o_dmi_previous setTitle: _NS("Previous")];
766
    [o_dmi_mute setTitle: _NS("Mute")];
767
 
768 769 770 771 772 773 774 775 776 777
    /* 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")];
778 779 780 781
}

- (void)applicationWillFinishLaunching:(NSNotification *)o_notification
{
782 783 784
    o_msg_lock = [[NSLock alloc] init];
    o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];

785
    /* FIXME: don't poll */
786
    interfaceTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
787
                                     target: self selector: @selector(manageIntf:)
788
                                   userInfo: nil repeats: FALSE] retain];
789

790 791
    /* 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
792

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

796
    /* check whether the user runs a valid version of OSX; alert is auto-released */
797
    if( MACOS_VERSION < 10.4f )
798 799 800 801 802 803 804 805 806 807 808 809 810
    {
        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];
    }

811
    vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
812
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
813

814 815
- (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
{
816
    BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
817
    NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
818 819 820 821
    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
822

823 824
    return( TRUE );
}
Derk-Jan Hartman's avatar
-  
Derk-Jan Hartman committed
825

826
- (NSString *)localizedString:(const char *)psz
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
827 828 829 830 831 832
{
    NSString * o_str = nil;

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

834
        if( o_str == NULL )
835 836 837 838
        {
            msg_Err( VLCIntf, "could not translate: %s", psz );
            return( @"" );
        }
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
839
    }
840
    else
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
841
    {
842 843
        msg_Warn( VLCIntf, "can't translate empty strings" );
        return( @"" );
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
844 845 846 847 848
    }

    return( o_str );
}

849 850
/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
851
{    
852
    if(!hasVisibleWindows)
853 854 855 856 857
        [o_window makeKeyAndOrderFront:self];

    return YES;
}

858 859
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
860
#ifdef UPDATE_CHECK
861
    /* Check for update silently on startup */
862
    if( !nib_update_loaded )
863
        nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner:self];
864

Rafaël Carré's avatar
Rafaël Carré committed
865
    if([o_update shouldCheckForUpdate])
866
        [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:nil];
867 868
#endif

869 870 871
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];
872

873
    [NSThread detachNewThreadSelector:@selector(lookForCrashLog) toTarget:self withObject:nil];
874 875
}

876 877 878 879 880 881 882 883 884 885 886
/* 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];
}

887 888 889 890
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
    /* Pause */
891
    if( p_intf->p_sys->i_play_status == PLAYING_S )
892 893 894 895 896 897 898
    {
        vlc_value_t val;
        val.i_int = config_GetInt( p_intf, "key-play-pause" );
        var_Set( p_intf->p_libvlc, "key-pressed", val );
    }
}

899 900
/* 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 */
901
- (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
902
{
903
    if(b_remote_button_hold)
904
    {
905
        switch([buttonIdentifierNumber intValue])
906
        {
907
            case kRemoteButtonRight_Hold:
908
                  [o_controls forward: self];
909 910
            break;
            case kRemoteButtonLeft_Hold:
911 912 913 914 915 916 917
                  [o_controls backward: self];
            break;
            case kRemoteButtonVolume_Plus_Hold:
                [o_controls volumeUp: self];
            break;
            case kRemoteButtonVolume_Minus_Hold:
                [o_controls volumeDown: self];
918
            break;
919
        }
920
        if(b_remote_button_hold)
921
        {
922
            /* trigger event */
923
            [self performSelector:@selector(executeHoldActionForRemoteButton:)
924
                         withObject:buttonIdentifierNumber
925
                         afterDelay:0.25];
926 927 928 929
        }
    }
}

930
/* Apple Remote callback */
931 932 933
- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
               pressedDown: (BOOL) pressedDown
                clickCount: (unsigned int) count
934 935 936 937
{
    switch( buttonIdentifier )
    {
        case kRemoteButtonPlay:
938
            if(count >= 2) {
939 940 941
                [o_controls toogleFullscreen:self];
            } else {
                [o_controls play: self];
942
            }
943
            break;
Eric Petit's avatar