VLCMainWindow.m 51.9 KB
Newer Older
1
/*****************************************************************************
2
 * VLCMainWindow.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2013 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
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * 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.
 *****************************************************************************/

28 29
#import "VLCMainWindow.h"

30
#import "intf.h"
31
#import "CompatibilityFixes.h"
32
#import "VLCCoreInteraction.h"
33
#import "VLCAudioEffectsWindowController.h"
David Fuhrmann's avatar
David Fuhrmann committed
34
#import "VLCMainMenu.h"
35
#import "VLCOpenWindowController.h"
36
#import "VLCPlaylist.h"
37
#import "SideBarItem.h"
38
#import <math.h>
39
#import <vlc_playlist.h>
40 41
#import <vlc_url.h>
#import <vlc_strings.h>
42
#import <vlc_services_discovery.h>
43
#import "PLModel.h"
44

Felix Paul Kühne's avatar
Felix Paul Kühne committed
45 46 47
#import "PXSourceList.h"
#import "PXSourceListDataSource.h"

48
#import "ControlsBar.h"
49
#import "VideoView.h"
50
#import "VLCVoutWindowController.h"
51 52


53
@interface VLCMainWindow() <PXSourceListDataSource, PXSourceListDelegate, NSOutlineViewDataSource, NSOutlineViewDelegate, NSWindowDelegate, NSAnimationDelegate, NSSplitViewDelegate>
54
{
55 56 57 58 59
    BOOL videoPlaybackEnabled;
    BOOL dropzoneActive;
    BOOL splitViewRemoved;
    BOOL minimizedView;

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    BOOL b_video_playback_enabled;
    BOOL b_dropzone_active;
    BOOL b_splitview_removed;
    BOOL b_minimized_view;

    CGFloat f_lastSplitViewHeight;
    CGFloat f_lastLeftSplitViewWidth;

    NSMutableArray *o_sidebaritems;

    /* this is only true, when we have NO video playing inside the main window */

    BOOL b_podcastView_displayed;

    VLCColorView * o_color_backdrop;

    NSRect frameBeforePlayback;
}
78 79 80
- (void)resizePlaylistAfterCollapse;
- (void)makeSplitViewVisible;
- (void)makeSplitViewHidden;
81 82
- (void)showPodcastControls;
- (void)hidePodcastControls;
83 84
@end

85
static const float f_min_window_height = 307.;
86

87 88
@implementation VLCMainWindow

89 90 91
#pragma mark -
#pragma mark Initialization

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

97
    key = config_GetPsz(getIntf(), 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 121 122 123 124
        ![self isEvent: o_event forKey: "key-vol-mute"] &&
        ![self isEvent: o_event forKey: "key-prev"] &&
        ![self isEvent: o_event forKey: "key-next"] &&
        ![self isEvent: o_event forKey: "key-jump+short"] &&
        ![self isEvent: o_event forKey: "key-jump-short"]) {
125 126
        /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
         so we perform the menu equivalent now. */
127
        if ([[NSApp mainMenu] performKeyEquivalent:o_event])
128 129
            return TRUE;
    }
130 131
    else
        b_force = YES;
132

133 134 135
    VLCCoreInteraction *coreInteraction = [VLCCoreInteraction sharedInstance];
    return [coreInteraction hasDefinedShortcutKey:o_event force:b_force] ||
           [coreInteraction keyEvent:o_event];
136 137 138 139
}

- (void)dealloc
{
140
    [[NSNotificationCenter defaultCenter] removeObserver: self];
141 142 143 144
}

- (void)awakeFromNib
{
145 146
    [super awakeFromNib];

147 148 149 150 151 152 153 154
    /*
     * General setup
     */

    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    BOOL splitViewShouldBeHidden = NO;
155

156 157
    [self setDelegate:self];
    [self setRestorable:NO];
158 159 160
    // TODO: useOptimizedDrawing deprecated since 10.10, but no
    // documentation provided what do use instead.
    // see radar://23047516
161 162 163
    [self useOptimizedDrawing:YES];
    [self setExcludedFromWindowsMenu:YES];
    [self setAcceptsMouseMovedEvents:YES];
164 165
    [self setFrameAutosaveName:@"mainwindow"];

166
    _nativeFullscreenMode = var_InheritBool(getIntf(), "macosx-nativefullscreenmode");
167
    b_dropzone_active = YES;
168

169
    // Playlist setup
170
    VLCPlaylist *playlist = [[VLCMain sharedInstance] playlist];
171 172
    [playlist setOutlineView:(VLCPlaylistView *)_outlineView];
    [playlist setPlaylistHeaderView:_outlineView.headerView];
173
    [self setNextResponder:playlist];
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

    // (Re)load sidebar for the first time and select first item
    [self reloadSidebar];
    [_sidebarView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];

    // Setup view frame sizes
    [_dropzoneView setFrame:_playlistScrollView.frame];
    [_splitViewLeft setFrame:_sidebarView.frame];

    /*
     * Set up translatable strings for the UI elements
     */

    // Window title
    [self setTitle:_NS("VLC media player")];

    // Search Field
    [_searchField setToolTip:_NS("Search in Playlist")];
    [_searchField.cell setPlaceholderString:_NS("Search")];
    [_searchField.cell accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.")
                                        forAttribute:NSAccessibilityDescriptionAttribute];

    // Dropzone
    [_dropzoneLabel setStringValue:_NS("Drop media here")];
    [_dropzoneImageView setImage:imageFromRes(@"dropzone")];
    [_dropzoneButton setTitle:_NS("Open media...")];
    [_dropzoneButton.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];

    // Podcast view
    [_podcastAddButton setTitle:_NS("Subscribe")];
    [_podcastRemoveButton setTitle:_NS("Unsubscribe")];

    // Podcast subscribe window
    [_podcastSubscribeTitle setStringValue:_NS("Subscribe to a podcast")];
    [_podcastSubscribeSubtitle setStringValue:_NS("Enter URL of the podcast to subscribe to:")];
    [_podcastSubscribeOkButton setTitle:_NS("Subscribe")];
    [_podcastSubscribeCancelButton setTitle:_NS("Cancel")];

    // Podcast unsubscribe window
    [_podcastUnsubscirbeTitle setStringValue:_NS("Unsubscribe from a podcast")];
    [_podcastUnsubscribeSubtitle setStringValue:_NS("Select the podcast you would like to unsubscribe from:")];
    [_podcastUnsubscribeOkButton setTitle:_NS("Unsubscribe")];
    [_podcastUnsubscribeCancelButton setTitle:_NS("Cancel")];
218

219
    /* interface builder action */
220
    CGFloat f_threshold_height = f_min_video_height + [self.controlsBar height];
221
    if (self.darkInterface)
222
        f_threshold_height += [self.titlebarView frame].size.height;
223
    if ([[self contentView] frame].size.height < f_threshold_height)
224
        splitViewShouldBeHidden = YES;
225

226
    // Set that here as IB seems to be buggy
227
    if (self.darkInterface)
228
        [self setContentMinSize:NSMakeSize(604., f_min_window_height + [self.titlebarView frame].size.height)];
229
    else
230
        [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
231

232 233
    /* the default small size of the search field is slightly different on Lion, let's work-around that */
    NSRect frame;
234
    frame = [_searchField frame];
235 236
    frame.origin.y = frame.origin.y + 2.0;
    frame.size.height = frame.size.height - 1.0;
237
    [_searchField setFrame:frame];
238

239 240 241 242
    _fspanel = [[VLCFSPanel alloc] initWithContentRect:NSMakeRect(110.,267.,549.,87.)
                                             styleMask:NSTexturedBackgroundWindowMask
                                               backing:NSBackingStoreBuffered
                                                 defer:YES];
243

244
    /* make sure we display the desired default appearance when VLC launches for the first time */
245
    if (![defaults objectForKey:@"VLCFirstRun"]) {
246 247
        [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];

248
        [_sidebarView expandItem:nil expandChildren:YES];
249
        [_fspanel center];
250

Felix Paul Kühne's avatar
Felix Paul Kühne committed
251
        NSAlert *albumArtAlert = [NSAlert alertWithMessageText:_NS("Check for album art and metadata?") defaultButton:_NS("Enable Metadata Retrieval") alternateButton:_NS("No, Thanks") otherButton:nil informativeTextWithFormat:@"%@",_NS("VLC can check online for album art and metadata to enrich your playback experience, e.g. by providing track information when playing Audio CDs. To provide this functionality, VLC will send information about your contents to trusted services in an anonymized form.")];
252
        NSInteger returnValue = [albumArtAlert runModal];
253
        config_PutInt(getIntf(), "metadata-network-access", returnValue == NSAlertDefaultReturn);
254 255
    }

256
    if (self.darkInterface) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
257 258
        [defaultCenter addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
        [defaultCenter addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
259

260 261
        [self setBackgroundColor: [NSColor clearColor]];
        [self setOpaque: NO];
262 263
        [self display];
        [self setHasShadow:NO];
264
        [self setHasShadow:YES];
265

266
        NSRect winrect = [self frame];
267
        CGFloat f_titleBarHeight = [self.titlebarView frame].size.height;
268

269
        [self.titlebarView setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
270
                                              winrect.size.width, f_titleBarHeight)];
271
        [[self contentView] addSubview: self.titlebarView positioned: NSWindowAbove relativeTo: _splitView];
272

273
        if (winrect.size.height > 100) {
274
            [self setFrame: winrect display:YES animate:YES];
275
            self.previousSavedFrame = winrect;
276
        }
277

278
        winrect = _splitView.frame;
279
        winrect.size.height = winrect.size.height - f_titleBarHeight;
280
        [_splitView setFrame: winrect];
281
        [self.videoView setFrame: winrect];
282

283 284
        o_color_backdrop = [[VLCColorView alloc] initWithFrame:_splitView.frame];
        [[self contentView] addSubview:o_color_backdrop positioned:NSWindowBelow relativeTo:_splitView];
285
        [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
286
    } else {
287 288 289
        [self.videoView setFrame:_splitView.frame];
        [_playlistScrollView setBorderType:NSNoBorder];
        [_sidebarScrollView setBorderType:NSNoBorder];
290
    }
291

Felix Paul Kühne's avatar
Felix Paul Kühne committed
292 293 294
    [defaultCenter addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
    [defaultCenter addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
    [defaultCenter addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
295
    [defaultCenter addObserver: self selector: @selector(mainSplitViewDidResizeSubviews:) name: NSSplitViewDidResizeSubviewsNotification object:_splitView];
296

297 298
    if (splitViewShouldBeHidden) {
        [self hideSplitView:YES];
299
        f_lastSplitViewHeight = 300;
300
    }
301 302

    /* sanity check for the window size */
Felix Paul Kühne's avatar
Felix Paul Kühne committed
303
    frame = [self frame];
304 305
    NSSize screenSize = [[self screen] frame].size;
    if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
306
        self.nativeVideoSize = screenSize;
307 308
        [self resizeWindow];
    }
309

310
    /* update fs button to reflect state for next startup */
311
    if (var_InheritBool(pl_Get(getIntf()), "fullscreen"))
312
        [self.controlsBar setFullscreenState:YES];
313 314

    /* restore split view */
315
    f_lastLeftSplitViewWidth = 200;
316
    /* trick NSSplitView implementation, which pretends to know better than us */
317
    if (!config_GetInt(getIntf(), "macosx-show-sidebar"))
318
        [self performSelector:@selector(toggleLeftSubSplitView) withObject:nil afterDelay:0.05];
319 320
}

321
#pragma mark -
322
#pragma mark appearance management
323

324 325 326
- (void)reloadSidebar
{
    BOOL isAReload = NO;
327
    if (o_sidebaritems)
328 329 330 331 332
        isAReload = YES;

    o_sidebaritems = [[NSMutableArray alloc] init];
    SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
    SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
333
    [playlistItem setIcon: imageFromRes(@"sidebar-playlist")];
334
    SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
335
    [medialibraryItem setIcon: imageFromRes(@"sidebar-playlist")];
336 337 338 339 340
    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"];

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
341
    /* SD subnodes, inspired by the Qt intf */
342 343
    char **ppsz_longnames = NULL;
    int *p_categories = NULL;
344
    char **ppsz_names = vlc_sd_GetNames(pl_Get(getIntf()), &ppsz_longnames, &p_categories);
345
    if (!ppsz_names)
346
        msg_Err(getIntf(), "no sd item found"); //TODO
347 348 349 350 351 352 353 354
    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];
    NSString *o_identifier;
    for (; ppsz_name && *ppsz_name; ppsz_name++, ppsz_longname++, p_category++) {
355
        o_identifier = toNSStr(*ppsz_name);
356 357 358
        switch (*p_category) {
            case SD_CAT_INTERNET:
                [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
359
                [[internetItems lastObject] setIcon: imageFromRes(@"sidebar-podcast")];
360
                [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
361
                [[internetItems lastObject] setUntranslatedTitle: toNSStr(*ppsz_longname)];
362 363 364
                break;
            case SD_CAT_DEVICES:
                [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
365
                [[devicesItems lastObject] setIcon: imageFromRes(@"sidebar-local")];
366
                [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
367
                [[devicesItems lastObject] setUntranslatedTitle: toNSStr(*ppsz_longname)];
368 369 370
                break;
            case SD_CAT_LAN:
                [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
371
                [[lanItems lastObject] setIcon: imageFromRes(@"sidebar-local")];
372
                [[lanItems lastObject] setSdtype: SD_CAT_LAN];
373
                [[lanItems lastObject] setUntranslatedTitle: toNSStr(*ppsz_longname)];
374 375 376 377
                break;
            case SD_CAT_MYCOMPUTER:
                [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
                if (!strncmp(*ppsz_name, "video_dir", 9))
378
                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-movie")];
379
                else if (!strncmp(*ppsz_name, "audio_dir", 9))
380
                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-music")];
381
                else if (!strncmp(*ppsz_name, "picture_dir", 11))
382
                    [[mycompItems lastObject] setIcon: imageFromRes(@"sidebar-pictures")];
383 384
                else
                    [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
385
                [[mycompItems lastObject] setUntranslatedTitle: toNSStr(*ppsz_longname)];
386 387 388
                [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
                break;
            default:
389
                msg_Warn(getIntf(), "unknown SD type found, skipping (%s)", *ppsz_name);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
                break;
        }

        free(*ppsz_name);
        free(*ppsz_longname);
    }
    [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
    [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
    [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
    [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
    free(ppsz_names);
    free(ppsz_longnames);
    free(p_categories);

    [libraryItem setChildren: [NSArray arrayWithObjects:playlistItem, medialibraryItem, nil]];
    [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];

415 416 417
    [_sidebarView reloadData];
    [_sidebarView setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
    [_sidebarView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
418

419 420 421 422
    [_sidebarView setAutosaveName:@"mainwindow-sidebar"];
    [_sidebarView setDataSource:self];
    [_sidebarView setDelegate:self];
    [_sidebarView setAutosaveExpandedItems:YES];
423

424
    [_sidebarView expandItem:libraryItem expandChildren:YES];
425 426

    if (isAReload) {
427
        [_sidebarView expandItem:nil expandChildren:YES];
428 429 430
    }
}

431 432
- (void)resizePlaylistAfterCollapse
{
433
    // no animation here since we might be in the middle of another resize animation
434
    NSRect rightSplitRect = [_splitViewRight frame];
435

436
    NSRect plrect;
437 438 439
    plrect.size.height = rightSplitRect.size.height - 20.0; // actual pl top bar height, which differs from its frame
    plrect.size.width = rightSplitRect.size.width;
    plrect.origin.x = plrect.origin.y = 0.;
440

441
    NSRect dropzoneboxRect = _dropzoneBox.frame;
442 443 444
    dropzoneboxRect.origin.x = (plrect.size.width - dropzoneboxRect.size.width) / 2;
    dropzoneboxRect.origin.y = (plrect.size.height - dropzoneboxRect.size.height) / 2;

445 446
    [_dropzoneView setFrame:plrect];
    [_dropzoneBox setFrame:dropzoneboxRect];
447 448

    if (b_podcastView_displayed) {
449 450
        plrect.size.height -= [_podcastView frame].size.height;
        plrect.origin.y = [_podcastView frame].size.height;
451
    }
452
    [_playlistScrollView setFrame:plrect];
453

454 455
    [_dropzoneView setNeedsDisplay:YES];
    [_playlistScrollView setNeedsDisplay:YES];
456 457
}

458 459
- (void)makeSplitViewVisible
{
460
    if (self.darkInterface)
461
        [self setContentMinSize: NSMakeSize(604., f_min_window_height + [self.titlebarView frame].size.height)];
462
    else
463
        [self setContentMinSize: NSMakeSize(604., f_min_window_height)];
464 465

    NSRect old_frame = [self frame];
466
    CGFloat newHeight = [self minSize].height;
467
    if (old_frame.size.height < newHeight) {
468 469 470 471
        NSRect new_frame = old_frame;
        new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
        new_frame.size.height = newHeight;

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

475 476
    [self.videoView setHidden:YES];
    [_splitView setHidden:NO];
477
    if (self.nativeFullscreenMode && [self fullscreen]) {
478
        [[self.controlsBar bottomBarView] setHidden:NO];
479
        [self.fspanel setNonActive:nil];
480 481
    }

482
    [self makeFirstResponder:_playlistScrollView];
483 484 485 486
}

- (void)makeSplitViewHidden
{
487
    if (self.darkInterface)
488
        [self setContentMinSize: NSMakeSize(604., f_min_video_height + [self.titlebarView frame].size.height)];
489
    else
490
        [self setContentMinSize: NSMakeSize(604., f_min_video_height)];
491

492 493
    [_splitView setHidden:YES];
    [self.videoView setHidden:NO];
494
    if (self.nativeFullscreenMode && [self fullscreen]) {
495
        [[self.controlsBar bottomBarView] setHidden:YES];
496
        [self.fspanel setActive:nil];
497
    }
498

499
    if ([[self.videoView subviews] count] > 0)
500
        [self makeFirstResponder: [[self.videoView subviews] firstObject]];
501 502
}

503 504

- (void)changePlaylistState:(VLCPlaylistStateEvent)event
505
{
506 507
    // Beware, this code is really ugly

508
    msg_Dbg(getIntf(), "toggle playlist from state: removed splitview %i, minimized view %i. Event %i", b_splitview_removed, b_minimized_view, event);
509 510
    if (![self isVisible] && event == psUserMenuEvent) {
        [self makeKeyAndOrderFront: nil];
511
        return;
512 513
    }

514
    BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
515
    BOOL b_restored = NO;
516

517
    // ignore alt if triggered through main menu shortcut
518
    BOOL b_have_alt_key = ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0;
519
    if (event == psUserMenuEvent)
520 521
        b_have_alt_key = NO;

522 523 524 525
    // eUserMenuEvent is now handled same as eUserEvent
    if(event == psUserMenuEvent)
        event = psUserEvent;

526
    if (b_dropzone_active && b_have_alt_key) {
527 528 529 530
        [self hideDropZone];
        return;
    }

531 532
    if (!(self.nativeFullscreenMode && self.fullscreen) && !b_splitview_removed && ((b_have_alt_key && b_activeVideo)
                                                                              || (self.nonembedded && event == psUserEvent)
533 534
                                                                              || (!b_activeVideo && event == psUserEvent)
                                                                              || (b_minimized_view && event == psVideoStartedOrStoppedEvent))) {
535 536
        // for starting playback, window is resized through resized events
        // for stopping playback, resize through reset to previous frame
537
        [self hideSplitView: event != psVideoStartedOrStoppedEvent];
538 539
        b_minimized_view = NO;
    } else {
540
        if (b_splitview_removed) {
541
            if (!self.nonembedded || (event == psUserEvent && self.nonembedded))
542
                [self showSplitView: event != psVideoStartedOrStoppedEvent];
543

544
            if (event != psUserEvent)
545 546 547 548 549 550
                b_minimized_view = YES;
            else
                b_minimized_view = NO;

            if (b_activeVideo)
                b_restored = YES;
551 552
        }

553 554
        if (!self.nonembedded) {
            if (([self.videoView isHidden] && b_activeVideo) || b_restored || (b_activeVideo && event != psUserEvent))
555
                [self makeSplitViewHidden];
556
            else
557
                [self makeSplitViewVisible];
558
        } else {
559 560
            [_splitView setHidden: NO];
            [_playlistScrollView setHidden: NO];
561
            [self.videoView setHidden: YES];
562
        }
563
    }
564

565
    msg_Dbg(getIntf(), "toggle playlist to state: removed splitview %i, minimized view %i", b_splitview_removed, b_minimized_view);
566 567
}

568 569 570 571 572
- (IBAction)dropzoneButtonAction:(id)sender
{
    [[[VLCMain sharedInstance] open] openFileGeneric];
}

573 574 575
#pragma mark -
#pragma mark overwritten default functionality

576 577
- (void)windowResizedOrMoved:(NSNotification *)notification
{
578
    [self saveFrameUsingName:[self frameAutosaveName]];
579 580
}

581 582
- (void)applicationWillTerminate:(NSNotification *)notification
{
583
    config_PutInt(getIntf(), "macosx-show-sidebar", ![_splitView isSubviewCollapsed:_splitViewLeft]);
584
    [self saveFrameUsingName:[self frameAutosaveName]];
585 586
}

587

588 589
- (void)someWindowWillClose:(NSNotification *)notification
{
590
    id obj = [notification object];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
591

592 593
    // hasActiveVideo is defined for VLCVideoWindowCommon and subclasses
    if ([obj respondsToSelector:@selector(hasActiveVideo)] && [obj hasActiveVideo]) {
594 595 596
        if ([[VLCMain sharedInstance] activeVideoPlayback])
            [[VLCCoreInteraction sharedInstance] stop];
    }
597 598 599 600
}

- (void)someWindowWillMiniaturize:(NSNotification *)notification
{
601
    if (config_GetInt(getIntf(), "macosx-pause-minimized")) {
602
        id obj = [notification object];
603

604
        if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !self.nonembedded)) {
605
            if ([[VLCMain sharedInstance] activeVideoPlayback])
606 607
                [[VLCCoreInteraction sharedInstance] pause];
        }
608 609 610
    }
}

611 612
#pragma mark -
#pragma mark Update interface and respond to foreign events
613 614
- (void)showDropZone
{
615
    b_dropzone_active = YES;
616 617 618
    [_splitViewRight addSubview:_dropzoneView positioned:NSWindowAbove relativeTo:_playlistScrollView];
    [_dropzoneView setFrame:_playlistScrollView.frame];
    [_playlistScrollView setHidden:YES];
619 620 621 622
}

- (void)hideDropZone
{
623
    b_dropzone_active = NO;
624 625
    [_dropzoneView removeFromSuperview];
    [_playlistScrollView setHidden:NO];
626 627
}

628
- (void)hideSplitView:(BOOL)resize
629
{
630 631 632
    // cancel pending pl resizes, in case of fast toggle between both modes
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];

633
    if (resize) {
634
        NSRect winrect = [self frame];
635
        f_lastSplitViewHeight = [_splitView frame].size.height;
636 637
        winrect.size.height = winrect.size.height - f_lastSplitViewHeight;
        winrect.origin.y = winrect.origin.y + f_lastSplitViewHeight;
638
        [self setFrame:winrect display:YES animate:YES];
639 640
    }

641
    if (self.darkInterface) {
642 643
        [self setContentMinSize: NSMakeSize(604., [self.controlsBar height] + [self.titlebarView frame].size.height)];
        [self setContentMaxSize: NSMakeSize(FLT_MAX, [self.controlsBar height] + [self.titlebarView frame].size.height)];
644
    } else {
645 646
        [self setContentMinSize: NSMakeSize(604., [self.controlsBar height])];
        [self setContentMaxSize: NSMakeSize(FLT_MAX, [self.controlsBar height])];
647
    }
648

649 650 651
    b_splitview_removed = YES;
}

652
- (void)showSplitView:(BOOL)resize
653
{
654
    [self updateWindow];
655
    if (self.darkInterface)
656
        [self setContentMinSize:NSMakeSize(604., f_min_window_height + [self.titlebarView frame].size.height)];
657
    else
658
        [self setContentMinSize:NSMakeSize(604., f_min_window_height)];
659
    [self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
660

661
    if (resize) {
662 663
        NSRect winrect;
        winrect = [self frame];
664 665
        winrect.size.height = winrect.size.height + f_lastSplitViewHeight;
        winrect.origin.y = winrect.origin.y - f_lastSplitViewHeight;
666
        [self setFrame:winrect display:YES animate:YES];
667
    }
668

669 670
    // cancel pending pl resizes, in case of fast toggle between both modes
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resizePlaylistAfterCollapse) object:nil];
671 672 673 674 675
    [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];

    b_splitview_removed = NO;
}

676 677
- (void)updateTimeSlider
{
678 679
    [self.controlsBar updateTimeSlider];
    [self.fspanel updatePositionAndTime];
680

681 682 683
    [[[VLCMain sharedInstance] voutController] updateControlsBarsUsingBlock:^(VLCControlsBarCommon *controlsBar) {
        [controlsBar updateTimeSlider];
    }];
684 685

    [[VLCCoreInteraction sharedInstance] updateAtoB];
686 687
}

688
- (void)updateName
689
{
690
    input_thread_t *p_input;
691
    p_input = pl_CurrentInput(getIntf());
692
    if (p_input) {
693
        NSString *aString = @"";
694

695 696
        if (!config_GetPsz(getIntf(), "video-title")) {
            char *format = var_InheritString(getIntf(), "input-title-format");
697
            if (format) {
698
                char *formated = vlc_strfinput(p_input, format);
699 700 701 702
                free(format);
                aString = toNSStr(formated);
                free(formated);
            }
703
        } else
704
            aString = toNSStr(config_GetPsz(getIntf(), "video-title"));
705

706
        char *uri = input_item_GetURI(input_GetItem(p_input));
707

708
        NSURL * o_url = [NSURL URLWithString:toNSStr(uri)];
709
        if ([o_url isFileURL]) {
710
            [self setRepresentedURL: o_url];
711 712 713
            [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
                [o_window setRepresentedURL:o_url];
            }];
714
        } else {
715
            [self setRepresentedURL: nil];
716 717 718
            [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
                [o_window setRepresentedURL:nil];
            }];
719
        }
720
        free(uri);
721

722
        if ([aString isEqualToString:@""]) {
723 724 725 726
            if ([o_url isFileURL])
                aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
            else
                aString = [o_url absoluteString];
727
        }
728

729 730 731 732 733 734
        if ([aString length] > 0) {
            [self setTitle: aString];
            [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
                [o_window setTitle:aString];
            }];

735
            [self.fspanel setStreamTitle: aString];
736
        } else {
737
            [self setTitle: _NS("VLC media player")];
738 739
            [self setRepresentedURL: nil];
        }
740

741 742
        vlc_object_release(p_input);
    } else {
743
        [self setTitle: _NS("VLC media player")];
744
        [self setRepresentedURL: nil];
745
    }
746 747
}

748 749
- (void)updateWindow
{
750
    [self.controlsBar updateControls];
751 752 753
    [[[VLCMain sharedInstance] voutController] updateControlsBarsUsingBlock:^(VLCControlsBarCommon *controlsBar) {
        [controlsBar updateControls];
    }];
754

755 756
    bool b_seekable = false;

757
    playlist_t *p_playlist = pl_Get(getIntf());
758
    input_thread_t *p_input = playlist_CurrentInput(p_playlist);
759
    if (p_input) {
760
        /* seekable streams */
761
        b_seekable = var_GetBool(p_input, "can-seek");
762

763
        vlc_object_release(p_input);
764 765
    }

766
    [self updateTimeSlider];
767 768
    if ([self.fspanel respondsToSelector:@selector(setSeekable:)])
        [self.fspanel setSeekable: b_seekable];
Felix Paul Kühne's avatar