MainWindow.m 70.1 KB
Newer Older
1
/*****************************************************************************
Pere Orga's avatar
Pere Orga committed
2
 * MainWindow.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2012 VLC authors and VideoLAN
5
6
7
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8
9
10
 *          Jon Lech Johansen <jon-vl@nanocrew.net>
 *          Christophe Massiot <massiot@via.ecp.fr>
 *          Derk-Jan Hartman <hartman at videolan.org>
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *
 * 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.
 *
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

27
#import "CompatibilityFixes.h"
28
#import "MainWindow.h"
29
30
31
32
#import "intf.h"
#import "CoreInteraction.h"
#import "AudioEffects.h"
#import "MainMenu.h"
33
#import "open.h"
34
#import "controls.h" // TODO: remove me
35
#import "playlist.h"
36
#import "SideBarItem.h"
37
#import <math.h>
38
39
#import <vlc_playlist.h>
#import <vlc_aout_intf.h>
40
41
#import <vlc_url.h>
#import <vlc_strings.h>
42
#import <vlc_services_discovery.h>
43
#import <vlc_aout_intf.h>
44

45
#import "ControlsBar.h"
46
#import "VideoView.h"
47
48


49
50
51
52
53
54
55
@interface VLCMainWindow ()
- (void)resizePlaylistAfterCollapse;
- (void)makeSplitViewVisible;
- (void)makeSplitViewHidden;

@end

56
@implementation VLCMainWindow
57
58
static const float f_min_video_height = 70.0;

59
static VLCMainWindow *_o_sharedInstance = nil;
60

61
62
63
64
65
66
67
68
69
70
+ (VLCMainWindow *)sharedInstance
{
    return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
}

#pragma mark -
#pragma mark Initialization

- (id)init
{
71
    if (_o_sharedInstance) {
72
73
        [self dealloc];
        return _o_sharedInstance;
74
    } else
75
76
77
78
79
80
81
82
        _o_sharedInstance = [super init];

    return _o_sharedInstance;
}

- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
                  backing:(NSBackingStoreType)backingType defer:(BOOL)flag
{
83
84
    self = [super initWithContentRect:contentRect styleMask:styleMask
                              backing:backingType defer:flag];
85
    _o_sharedInstance = self;
86
87
88
89
90
91

    [[VLCMain sharedInstance] updateTogglePlaylistState];

    return self;
}

92
93
94
95
96
- (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
{
    char *key;
    NSString *o_key;

97
    key = config_GetPsz(VLCIntf, keyString);
98
    o_key = [NSString stringWithFormat:@"%s", key];
99
    FREENULL(key);
100

101
    unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
102

103
104
    NSString * characters = [o_event charactersIgnoringModifiers];
    if ([characters length] > 0) {
105
        return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
106
107
108
109
110
111
                (i_keyModifiers & NSShiftKeyMask)     == ([o_event modifierFlags] & NSShiftKeyMask) &&
                (i_keyModifiers & NSControlKeyMask)   == ([o_event modifierFlags] & NSControlKeyMask) &&
                (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
                (i_keyModifiers & NSCommandKeyMask)   == ([o_event modifierFlags] & NSCommandKeyMask);
    }
    return NO;
112
113
}

114
115
- (BOOL)performKeyEquivalent:(NSEvent *)o_event
{
116
    BOOL b_force = NO;
117
    // these are key events which should be handled by vlc core, but are attached to a main menu item
118
    if (![self isEvent: o_event forKey: "key-vol-up"] &&
119
        ![self isEvent: o_event forKey: "key-vol-down"] &&
120
        ![self isEvent: o_event forKey: "key-vol-mute"]) {
121
122
        /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
         so we perform the menu equivalent now. */
123
        if ([[NSApp mainMenu] performKeyEquivalent:o_event])
124
125
            return TRUE;
    }
126
127
    else
        b_force = YES;
128

129
    return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
130
           [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
131
132
133
134
}

- (void)dealloc
{
135
136
137
    if (b_dark_interface)
        [o_color_backdrop release];

138
    [[NSNotificationCenter defaultCenter] removeObserver: self];
139
    [o_sidebaritems release];
140

141
    if (o_extra_video_window) {
142
        [o_extra_video_window release];
143
144
        o_extra_video_window = nil;
    }
145

146
147
148
149
150
    if (o_detached_video_window) {
        [o_detached_video_window release];
        o_detached_video_window = nil;
    }

151
152
153
154
155
    [super dealloc];
}

- (void)awakeFromNib
{
156
    BOOL b_splitviewShouldBeHidden = NO;
157

158
    /* setup the styled interface */
159
    b_nativeFullscreenMode = NO;
160
#ifdef MAC_OS_X_VERSION_10_7
161
    if (!OSX_SNOW_LEOPARD)
162
        b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
163
#endif
164
    t_hide_mouse_timer = nil;
165
    [self useOptimizedDrawing: YES];
166
    
167
    [[o_search_fld cell] setPlaceholderString: _NS("Search")];
168
    [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
169

170
    [o_dropzone_btn setTitle: _NS("Open media...")];
171
    [[o_dropzone_btn cell] accessibilitySetOverrideValue:_NS("Click to open an advanced dialog to select the media to play. You can also drop files here to play.") forAttribute:NSAccessibilityDescriptionAttribute];
172
    [o_dropzone_lbl setStringValue: _NS("Drop media here")];
173

174
175
    [o_podcast_add_btn setTitle: _NS("Subscribe")];
    [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
176
177
178
179
    [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
    [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
    [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
    [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
180
181
182
183
    [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
    [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
    [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
    [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
184

185
    /* interface builder action */
186
    float f_threshold_height = f_min_video_height + [[o_controls_bar bottomBarView] frame].size.height;
187
    if (b_dark_interface)
188
        f_threshold_height += [o_titlebar_view frame].size.height;
189
    if ([[self contentView] frame].size.height < f_threshold_height)
190
        b_splitviewShouldBeHidden = YES;
191

192
193
    [self setDelegate: self];
    [self setExcludedFromWindowsMenu: YES];
194
    [self setAcceptsMouseMovedEvents: YES];
195
    // Set that here as IB seems to be buggy
196
    if (b_dark_interface) {
197
        [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
198
    } else {
199
        [self setContentMinSize:NSMakeSize(604., 288.)];
200
    }
201

202
    [self setTitle: _NS("VLC media player")];
203

204
    b_dropzone_active = YES;
205
206
    o_temp_view = [[NSView alloc] init];
    [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
207
    [o_dropzone_view setFrame: [o_playlist_table frame]];
208
    [o_left_split_view setFrame: [o_sidebar_view frame]];
209
    
210
    if (b_nativeFullscreenMode) {
211
        [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
212
    } else {
213
        [o_titlebar_view setFullscreenButtonHidden: YES];
214
    }
215

216
    if (!OSX_SNOW_LEOPARD) {
217
218
219
220
221
222
223
        /* the default small size of the search field is slightly different on Lion, let's work-around that */
        NSRect frame;
        frame = [o_search_fld frame];
        frame.origin.y = frame.origin.y + 2.0;
        frame.size.height = frame.size.height - 1.0;
        [o_search_fld setFrame: frame];
    }
224

225
226
227
228
    /* create the sidebar */
    o_sidebaritems = [[NSMutableArray alloc] init];
    SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
    SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
229
    [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
230
231
    SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
    [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
232
233
234
235
236
237
238
239
    SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
    SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
    SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
    SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];

    /* SD subnodes, inspired by the Qt4 intf */
    char **ppsz_longnames;
    int *p_categories;
240
    char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
241
    if (!ppsz_names)
242
        msg_Err(VLCIntf, "no sd item found"); //TODO
243
244
245
246
247
248
    char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
    int *p_category = p_categories;
    NSMutableArray *internetItems = [[NSMutableArray alloc] init];
    NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
    NSMutableArray *lanItems = [[NSMutableArray alloc] init];
    NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
249
    NSString *o_identifier;
250
    for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
251
        o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
252
253
        switch (*p_category) {
            case SD_CAT_INTERNET:
254
                    [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
255
                    if (!strncmp(*ppsz_name, "podcast", 7))
256
                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
257
258
                    else
                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
259
                    [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
260
                    [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
261
262
                break;
            case SD_CAT_DEVICES:
263
                    [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
264
                    [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
265
                    [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
266
                    [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
267
268
                break;
            case SD_CAT_LAN:
269
                    [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
270
                    [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
271
                    [[lanItems lastObject] setSdtype: SD_CAT_LAN];
272
                    [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
273
274
                break;
            case SD_CAT_MYCOMPUTER:
275
                    [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
276
                    if (!strncmp(*ppsz_name, "video_dir", 9))
277
                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
278
                    else if (!strncmp(*ppsz_name, "audio_dir", 9))
279
                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
280
                    else if (!strncmp(*ppsz_name, "picture_dir", 11))
281
                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
282
283
                    else
                        [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
284
                    [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
285
                    [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
286
287
                break;
            default:
288
                msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
289
290
291
                break;
        }

292
293
        free(*ppsz_name);
        free(*ppsz_longname);
294
295
296
297
298
299
300
301
302
    }
    [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
    [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
    [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
    [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
    [mycompItems release];
    [devicesItems release];
    [lanItems release];
    [internetItems release];
303
304
305
    free(ppsz_names);
    free(ppsz_longnames);
    free(p_categories);
306

307
    [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
308
309
310
311
312
313
314
315
316
317
318
    [o_sidebaritems addObject: libraryItem];
    if ([mycompItem hasChildren])
        [o_sidebaritems addObject: mycompItem];
    if ([devicesItem hasChildren])
        [o_sidebaritems addObject: devicesItem];
    if ([lanItem hasChildren])
        [o_sidebaritems addObject: lanItem];
    if ([internetItem hasChildren])
        [o_sidebaritems addObject: internetItem];

    [o_sidebar_view reloadData];
319
    [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
320
321
    [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
    [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
322

323
324
325
326
327
    [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
    [(PXSourceList *)o_sidebar_view setDataSource:self];
    [o_sidebar_view setDelegate:self];
    [o_sidebar_view setAutosaveExpandedItems:YES];

328
329
330
331
    [o_sidebar_view expandItem: libraryItem expandChildren: YES];

    /* make sure we display the desired default appearance when VLC launches for the first time */
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
332
    if (![defaults objectForKey:@"VLCFirstRun"]) {
333
334
335
336
337
338
339
        [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];

        NSUInteger i_sidebaritem_count = [o_sidebaritems count];
        for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
            [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
    }

340
    if (b_dark_interface) {
341
342
343
        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
        [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];

344
345
        [self setBackgroundColor: [NSColor clearColor]];
        [self setOpaque: NO];
346
347
        [self display];
        [self setHasShadow:NO];
348
        [self setHasShadow:YES];
349

350
        NSRect winrect = [self frame];
351
352
        CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;

353
354
        [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
                                              winrect.size.width, f_titleBarHeight)];
355
356
        [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];

357
        if (winrect.size.height > 100) {
358
359
360
            [self setFrame: winrect display:YES animate:YES];
            previousSavedFrame = winrect;
        }
361
362
363
364
365

        winrect = [o_split_view frame];
        winrect.size.height = winrect.size.height - f_titleBarHeight;
        [o_split_view setFrame: winrect];
        [o_video_view setFrame: winrect];
366

367
368
369
        o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
        [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
        [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
370
        
371
    } else {
372
        [o_video_view setFrame: [o_split_view frame]];
373
374
375
        [o_playlist_table setBorderType: NSNoBorder];
        [o_sidebar_scrollview setBorderType: NSNoBorder];
    }
376

377
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
378
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
379
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
380

381
    [o_split_view setAutosaveName:@"10thanniversary-splitview"];
382
    if (b_splitviewShouldBeHidden) {
383
        [self hideSplitView];
384
        i_lastSplitViewHeight = 300;
385
    }
386
387

    /* sanity check for the window size */
388
    NSRect frame = [self frame];
389
390
391
392
393
    NSSize screenSize = [[self screen] frame].size;
    if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
        nativeVideoSize = screenSize;
        [self resizeWindow];
    }
394
395
}

396
#pragma mark -
397

398
- (VLCMainWindowControlsBar *)controlsBar;
399
{
400
    return (VLCMainWindowControlsBar *)o_controls_bar;
401
402
}

403
404
405
- (void)resizePlaylistAfterCollapse
{
    NSRect plrect;
406
    plrect = [o_playlist_table frame];
407
    plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
408
    [[o_playlist_table animator] setFrame: plrect];
409
410
411

    NSRect rightSplitRect;
    rightSplitRect = [o_right_split_view frame];
412
    plrect = [o_dropzone_box frame];
413
414
    plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
    plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
415
    [[o_dropzone_box animator] setFrame: plrect];
416
417
}

418
419
- (void)makeSplitViewVisible
{
420
421
    if (b_dark_interface)
        [self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
422
    else
423
        [self setContentMinSize: NSMakeSize(604., 288.)];
424
425
426

    NSRect old_frame = [self frame];
    float newHeight = [self minSize].height;
427
    if (old_frame.size.height < newHeight) {
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
        NSRect new_frame = old_frame;
        new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
        new_frame.size.height = newHeight;

        [[self animator] setFrame: new_frame display: YES animate: YES];
    }

    [o_video_view setHidden: YES];
    [o_split_view setHidden: NO];
    [self makeFirstResponder: nil];

}

- (void)makeSplitViewHidden
{
443
444
    if (b_dark_interface)
        [self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
445
    else
446
        [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
447
448
449

    [o_split_view setHidden: YES];
    [o_video_view setHidden: NO];
450

451
    if ([[o_video_view subviews] count] > 0)
452
        [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
453
454
}

455
// only exception for an controls bar button action
456
457
- (IBAction)togglePlaylist:(id)sender
{
458
    if (![self isVisible] && sender != nil) {
459
        [self makeKeyAndOrderFront: sender];
460
        return;
461
462
    }

463
    BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
464
    BOOL b_restored = NO;
465

466
467
    // TODO: implement toggle playlist in this situation (triggerd via menu item).
    // but for now we block this case, to avoid displaying only the half
468
    if (b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil)
469
470
        return;

471
    if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0) {
472
473
474
475
        [self hideDropZone];
        return;
    }

476
477
478
479
    if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
                                                                              || (b_nonembedded && sender != nil)
                                                                              || (!b_activeVideo && sender != nil)
                                                                              || b_minimized_view))
480
        [self hideSplitView];
481
482
483
    else {
        if (b_splitview_removed) {
            if (!b_nonembedded || (sender != nil && b_nonembedded))
484
                [self showSplitView];
485
486
487
488
489
490
491
492

            if (sender == nil)
                b_minimized_view = YES;
            else
                b_minimized_view = NO;

            if (b_activeVideo)
                b_restored = YES;
493
494
        }

495
496
        if (!b_nonembedded) {
            if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil))
497
                [self makeSplitViewHidden];
498
            else
499
                [self makeSplitViewVisible];
500
        } else {
501
            [o_split_view setHidden: NO];
502
            [o_playlist_table setHidden: NO];
503
            [o_video_view setHidden: !b_activeVideo];
504
            if (b_activeVideo && [[o_video_view subviews] count] > 0)
505
                [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
506
        }
507
508
509
    }
}

510
511
512
513
514
- (IBAction)dropzoneButtonAction:(id)sender
{
    [[[VLCMain sharedInstance] open] openFileGeneric];
}

515
516
517
#pragma mark -
#pragma mark overwritten default functionality

518
519
520
521
522
- (void)windowResizedOrMoved:(NSNotification *)notification
{
    [self saveFrameUsingName: [self frameAutosaveName]];
}

523
524
525
526
527
- (void)applicationWillTerminate:(NSNotification *)notification
{
    [self saveFrameUsingName: [self frameAutosaveName]];
}

528

529
530
- (void)someWindowWillClose:(NSNotification *)notification
{
531
532
    id obj = [notification object];
    if (obj == o_detached_video_window || obj == o_extra_video_window || (obj == self && !b_nonembedded)) {
533
534
535
        if ([[VLCMain sharedInstance] activeVideoPlayback])
            [[VLCCoreInteraction sharedInstance] stop];
    }
536
537
538
539
}

- (void)someWindowWillMiniaturize:(NSNotification *)notification
{
540
    if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
541
542
        id obj = [notification object];
        if (obj == o_detached_video_window || obj == o_extra_video_window || (obj == self && !b_nonembedded)) {
543
            if ([[VLCMain sharedInstance] activeVideoPlayback])
544
545
                [[VLCCoreInteraction sharedInstance] pause];
        }
546
547
548
    }
}

549
550
551
552
553
554
- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
{
    id videoWindow = [o_video_view window];
    if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
        return proposedFrameSize;

555
    // needed when entering lion fullscreen mode
556
    if (b_fullscreen)
557
        return proposedFrameSize;
558

559
    if ([[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]) {
560
561
562
563
564
        NSRect videoWindowFrame = [videoWindow frame];
        NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
        NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
        float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
        float marginx = contentRect.size.width - viewRect.size.width;
565
        if (b_dark_interface)// && b_video_deco)
566
            marginy += [o_titlebar_view frame].size.height;
567
568
569
570
571
572
573

        proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
    }

    return proposedFrameSize;
}

574
575
#pragma mark -
#pragma mark Update interface and respond to foreign events
576
577
- (void)showDropZone
{
578
    b_dropzone_active = YES;
579
    [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
580
    [o_dropzone_view setFrame: [o_playlist_table frame]];
581
    [[o_playlist_table animator] setHidden:YES];
582
583
584
585
}

- (void)hideDropZone
{
586
    b_dropzone_active = NO;
587
    [o_dropzone_view removeFromSuperview];
588
    [[o_playlist_table animator] setHidden: NO];
589
590
}

591
592
593
594
595
596
597
598
- (void)hideSplitView
{
    NSRect winrect = [self frame];
    i_lastSplitViewHeight = [o_split_view frame].size.height;
    winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
    winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
    [self setFrame: winrect display: YES animate: YES];
    [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
599
    if (b_dark_interface) {
600
601
        [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
        [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
602
    } else {
603
604
        [self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height)];
        [self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height)];
605
    }
606

607
608
609
610
611
    b_splitview_removed = YES;
}

- (void)showSplitView
{
612
    [self updateWindow];
613
    if (b_dark_interface)
614
        [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
615
    else
616
617
        [self setContentMinSize:NSMakeSize(604., 288.)];
    [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
618
619
620
621
622
623
624
625
626
627
628
629

    NSRect winrect;
    winrect = [self frame];
    winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
    winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
    [self setFrame: winrect display: YES animate: YES];

    [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];

    b_splitview_removed = NO;
}

630
631
- (void)updateTimeSlider
{
632
633
    [o_controls_bar updateTimeSlider];
    [[self controlsBar] updatePosAndTimeInFSPanel:o_fspanel];
634
635
    if (o_detached_video_window)
        [[o_detached_video_window controlsBar] updateTimeSlider];
636
637
}

638
- (void)updateName
639
640
{
    input_thread_t * p_input;
641
642
    p_input = pl_CurrentInput(VLCIntf);
    if (p_input) {
643
        NSString *aString;
644
645
646
        char *format = var_InheritString(VLCIntf, "input-title-format");
        char *formated = str_format_meta(pl_Get(VLCIntf), format);
        free(format);
647
        aString = [NSString stringWithUTF8String:formated];
648
        free(formated);
649

650
        char *uri = input_item_GetURI(input_GetItem(p_input));
651

652
        NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
653
        if ([o_url isFileURL]) {
654
            [self setRepresentedURL: o_url];
655
656
            if (o_detached_video_window)
                [o_detached_video_window setRepresentedURL: o_url];
657
        } else {
658
            [self setRepresentedURL: nil];
659
660
            if (o_detached_video_window)
                [o_detached_video_window setRepresentedURL: nil];
661
        }
662
        free(uri);
663

664
        if ([aString isEqualToString:@""]) {
665
666
667
668
            if ([o_url isFileURL])
                aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
            else
                aString = [o_url absoluteString];
669
        }
670
671

        [self setTitle: aString];
672
        if (b_nonembedded && o_detached_video_window && [[VLCMain sharedInstance] activeVideoPlayback])
673
674
            [o_detached_video_window setTitle: aString];

675
        [o_fspanel setStreamTitle: aString];
676
677
        vlc_object_release(p_input);
    } else {
678
        [self setTitle: _NS("VLC media player")];
679
        [self setRepresentedURL: nil];
680
    }
681
682
}

683
684
- (void)updateWindow
{
685
    [o_controls_bar updateControls];
686
687
    if (o_detached_video_window)
        [[o_detached_video_window controlsBar] updateControls];
688

689
690
    bool b_seekable = false;

691
692
    playlist_t * p_playlist = pl_Get(VLCIntf);
    input_thread_t * p_input = playlist_CurrentInput(p_playlist);
693
    if (p_input) {
694
        /* seekable streams */
695
        b_seekable = var_GetBool(p_input, "can-seek");
696

697
        vlc_object_release(p_input);
698
699
    }

700
    [self updateTimeSlider];
701
    [o_fspanel setSeekable: b_seekable];
702
703

    PL_LOCK;
704
    if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
705
706
707
708
        [self hideDropZone];
    else
        [self showDropZone];
    PL_UNLOCK;
709
    [o_sidebar_view setNeedsDisplay:YES];
710
711
712
713
}

- (void)setPause
{
714
    [o_controls_bar setPause];
715
716
    if (o_detached_video_window)
        [[o_detached_video_window controlsBar] setPause];
717
    [o_fspanel setPause];
718
719
720
721
}

- (void)setPlay
{
722
    [o_controls_bar setPlay];
723
724
    if (o_detached_video_window)
        [[o_detached_video_window controlsBar] setPlay];
725
    [o_fspanel setPlay];
726
727
}

728
- (void)updateVolumeSlider
729
{
730
731
    [[self controlsBar] updateVolumeSlider];
    [o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
732
733
}

734
735
#pragma mark -
#pragma mark Video Output handling
736

737
- (VLCVoutView *)setupVout:(vout_window_t *)p_wnd
738
{
739
740
    BOOL b_video_deco = var_InheritBool(VLCIntf, "video-deco");
    BOOL b_video_wallpaper = var_InheritBool(VLCIntf, "video-wallpaper");
741
742
    VLCVoutView *o_vout_view;
    VLCVideoWindowCommon *o_new_video_window;
743

744
    // TODO: make lion fullscreen compatible with video-wallpaper and !embedded-video
745
746
747
748
    if ((b_video_wallpaper || !b_video_deco) && !b_nativeFullscreenMode) {
        // b_video_wallpaper is priorized over !b_video_deco

        msg_Dbg(VLCIntf, "Creating background / blank window");
749
750
        NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
        if (!screen)
751
            screen = [self screen];
752
753
754
755
756

        NSRect window_rect;
        if (b_video_wallpaper)
            window_rect = [screen frame];
        else
757
            window_rect = [self frame];
758

759
        if (o_extra_video_window)
760
761
            [o_extra_video_window release];

762
763
764
765
766
        NSUInteger mask = NSBorderlessWindowMask;
        if (!OSX_SNOW_LEOPARD && !b_video_deco)
            mask |= NSResizableWindowMask;

        BOOL b_no_video_deco_only = !b_video_wallpaper;
767
        o_extra_video_window = [[VLCVideoWindowCommon alloc] initWithContentRect:window_rect styleMask:mask backing:NSBackingStoreBuffered defer:YES];
768
769
770
771
772
        [o_extra_video_window setDelegate:self];

        if (b_video_wallpaper)
            [o_extra_video_window setLevel:CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];

773
        [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
774
775
        [o_extra_video_window setCanBecomeKeyWindow: !b_video_wallpaper];
        [o_extra_video_window setCanBecomeMainWindow: !b_video_wallpaper];
776
        [o_extra_video_window setAcceptsMouseMovedEvents: !b_video_wallpaper];
777
        [o_extra_video_window setMovableByWindowBackground: !b_video_wallpaper];
778
        [o_extra_video_window useOptimizedDrawing: YES];
779

780
781
782
783
784
785
        o_vout_view = [[VLCVoutView alloc] initWithFrame:[[o_extra_video_window contentView] bounds]];
        [o_vout_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
        [[o_extra_video_window contentView] addSubview:o_vout_view positioned:NSWindowAbove relativeTo:nil];
        [o_extra_video_window setVideoView:o_vout_view];

        o_new_video_window = o_extra_video_window;
786

787
788
789
        if (b_video_wallpaper)
            [o_extra_video_window orderBack:nil];
        else {
790
            [o_extra_video_window center];
791
            [o_extra_video_window setFrameAutosaveName:@"extra-videowindow"];
792
            [o_extra_video_window setContentMinSize: NSMakeSize(f_min_video_height, f_min_video_height)];
793
        }
794
795

        b_nonembedded = YES;
796
    } else {
797
        if (var_InheritBool(VLCIntf, "embedded-video") || b_nativeFullscreenMode) {
798
799
            o_vout_view = [o_video_view retain];
            o_new_video_window = self;
800
            b_nonembedded = NO;
801
        } else {
802
803
804
            if (!o_detached_video_window) {
                NSWindowController *o_controller = [[NSWindowController alloc] initWithWindowNibName:@"DetachedVideoWindow"];
                [o_controller loadWindow];
805
                o_detached_video_window = [(VLCDetachedVideoWindow *)[o_controller window] retain];
806
807
808
809
810
811
812
813
814
815
816
817
818
                [o_controller release];

                // event occurs before window is created, so call again
                [[VLCMain sharedInstance] playbackStatusUpdated];
            }

            [o_detached_video_window setDelegate: self];
            if (b_dark_interface) {
                [o_detached_video_window setContentMinSize: NSMakeSize(363., f_min_video_height + [[[o_detached_video_window controlsBar] bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
            } else {
                [o_detached_video_window setContentMinSize: NSMakeSize(363., f_min_video_height + [[[o_detached_video_window controlsBar] bottomBarView] frame].size.height)];
            }

819
820
            [o_detached_video_window setLevel:NSNormalWindowLevel];
            [o_detached_video_window useOptimizedDrawing: YES];
821

822
823
            o_vout_view = [[o_detached_video_window videoView] retain];
            o_new_video_window = o_detached_video_window;
824
            b_nonembedded = YES;
825
        }
826
    }