VLCLibraryWindow.m 28.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*****************************************************************************
 * VLCLibraryWindow.m: MacOS X interface module
 *****************************************************************************
 * Copyright (C) 2019 VLC authors and VideoLAN
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan -dot- org>
 *
 * 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.
 *****************************************************************************/

#import "VLCLibraryWindow.h"
24
#import "extensions/NSString+Helpers.h"
25 26
#import "extensions/NSFont+VLCAdditions.h"
#import "extensions/NSColor+VLCAdditions.h"
27
#import "extensions/NSView+VLCAdditions.h"
28
#import "main/VLCMain.h"
29

30
#import "playlist/VLCPlayerController.h"
31 32
#import "playlist/VLCPlaylistController.h"
#import "playlist/VLCPlaylistDataSource.h"
33
#import "playlist/VLCPlaylistSortingMenuController.h"
34

35
#import "library/VLCLibraryController.h"
36
#import "library/VLCLibraryAudioDataSource.h"
37
#import "library/VLCLibraryVideoDataSource.h"
38
#import "library/VLCLibraryCollectionViewItem.h"
39
#import "library/VLCLibraryModel.h"
40
#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
41
#import "library/VLCLibraryAlternativeAudioViewController.h"
42
#import "library/VLCLibrarySortingMenuController.h"
43

44
#import "media-source/VLCMediaSourceCollectionViewItem.h"
45 46
#import "media-source/VLCMediaSourceDataSource.h"

47
#import "views/VLCDragDropView.h"
48
#import "views/VLCRoundedCornerTextField.h"
49

50 51 52
#import "windows/mainwindow/VLCControlsBarCommon.h"
#import "windows/video/VLCFSPanelController.h"
#import "windows/video/VLCVoutView.h"
53
#import "windows/video/VLCVideoOutputProvider.h"
54 55 56
#import "windows/VLCOpenWindowController.h"
#import "windows/VLCOpenInputMetadata.h"

57
#import <vlc_common.h>
58
#import <vlc_url.h>
59

60 61
const CGFloat VLCLibraryWindowMinimalWidth = 604.;
const CGFloat VLCLibraryWindowMinimalHeight = 307.;
62 63
const CGFloat VLCLibraryWindowLargePlaylistRowHeight = 60.;
const CGFloat VLCLibraryWindowSmallPlaylistRowHeight = 45.;
64 65
const CGFloat VLCLibraryWindowSmallRowHeight = 24.;
const CGFloat VLCLibraryWindowLargeRowHeight = 50.;
66
const CGFloat VLCLibraryWindowDefaultPlaylistWidth = 340.;
67

68
@interface VLCLibraryWindow () <VLCDragDropTarget, NSSplitViewDelegate>
69 70
{
    VLCPlaylistDataSource *_playlistDataSource;
71
    VLCLibraryVideoDataSource *_libraryVideoDataSource;
72 73
    VLCLibraryAudioDataSource *_libraryAudioDataSource;
    VLCLibraryGroupDataSource *_libraryAudioGroupDataSource;
74
    VLCLibrarySortingMenuController *_librarySortingMenuController;
75
    VLCMediaSourceDataSource *_mediaSourceDataSource;
76
    VLCLibraryAlternativeAudioViewController *_alternativeAudioViewController;
77
    VLCPlaylistSortingMenuController *_playlistSortingMenuController;
78

79 80
    VLCPlaylistController *_playlistController;

81
    NSRect _windowFrameBeforePlayback;
82
    CGFloat _lastPlaylistWidthBeforeCollaps;
83 84

    VLCFSPanelController *_fspanel;
85 86
}
@end
87

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
static int ShowFullscreenController(vlc_object_t *p_this, const char *psz_variable,
                                    vlc_value_t old_val, vlc_value_t new_val, void *param)
{
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:VLCVideoWindowShouldShowFullscreenController
                                                                object:nil];
        });

        return VLC_SUCCESS;
    }
}

static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                          vlc_value_t old_val, vlc_value_t new_val, void *param)
{
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:VLCWindowShouldShowController
                                                                object:nil];
        });

        return VLC_SUCCESS;
    }
}

114 115 116 117
@implementation VLCLibraryWindow

- (void)awakeFromNib
{
118 119 120
    VLCMain *mainInstance = [VLCMain sharedInstance];
    _playlistController = [mainInstance playlistController];

121 122 123 124
    libvlc_int_t *libvlc = vlc_object_instance(getIntf());
    var_AddCallback(libvlc, "intf-toggle-fscontrol", ShowFullscreenController, (__bridge void *)self);
    var_AddCallback(libvlc, "intf-show", ShowController, (__bridge void *)self);

125 126 127 128 129 130 131 132 133
    self.videoView = [[VLCVoutView alloc] initWithFrame:self.mainSplitView.frame];
    self.videoView.hidden = YES;
    self.videoView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addSubview:self.videoView];
    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.videoView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.mainSplitView attribute:NSLayoutAttributeWidth multiplier:1. constant:1.]];
    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.videoView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.mainSplitView attribute:NSLayoutAttributeHeight multiplier:1. constant:1.]];
    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.videoView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.mainSplitView attribute:NSLayoutAttributeCenterX multiplier:1. constant:1.]];
    [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.videoView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.mainSplitView attribute:NSLayoutAttributeCenterY multiplier:1. constant:1.]];

134 135 136 137 138
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self
                           selector:@selector(shouldShowFullscreenController:)
                               name:VLCVideoWindowShouldShowFullscreenController
                             object:nil];
139 140 141 142
    [notificationCenter addObserver:self
                           selector:@selector(shouldShowController:)
                               name:VLCWindowShouldShowController
                             object:nil];
143 144 145 146
    [notificationCenter addObserver:self
                           selector:@selector(updateLibraryRepresentation:)
                               name:VLCLibraryModelAudioMediaListUpdated
                             object:nil];
147 148 149 150
    [notificationCenter addObserver:self
                           selector:@selector(updateLibraryRepresentation:)
                               name:VLCLibraryModelVideoMediaListUpdated
                             object:nil];
151 152 153 154
    [notificationCenter addObserver:self
                           selector:@selector(updateLibraryRepresentation:)
                               name:VLCLibraryModelRecentMediaListUpdated
                             object:nil];
155 156 157 158 159 160 161 162
    [notificationCenter addObserver:self
                           selector:@selector(shuffleStateUpdated:)
                               name:VLCPlaybackOrderChanged
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(repeatStateUpdated:)
                               name:VLCPlaybackRepeatChanged
                             object:nil];
163 164
    [notificationCenter addObserver:self
                           selector:@selector(updateViewCellDimensionsBasedOnSetting:)
165
                               name:VLCConfigurationChangedNotification
166
                             object:nil];
167

168 169 170 171 172 173
    if (@available(macOS 10_14, *)) {
        [[NSApplication sharedApplication] addObserver:self
                                            forKeyPath:@"effectiveAppearance"
                                               options:0
                                               context:nil];
    }
174

175 176 177
    _fspanel = [[VLCFSPanelController alloc] init];
    [_fspanel showWindow:self];

178
    _segmentedTitleControl.segmentCount = 5;
179
    [_segmentedTitleControl setTarget:self];
180
    [_segmentedTitleControl setAction:@selector(segmentedControlAction:)];
181 182
    [_segmentedTitleControl setLabel:_NS("Video") forSegment:0];
    [_segmentedTitleControl setLabel:_NS("Music") forSegment:1];
183 184 185
    [_segmentedTitleControl setLabel:_NS("Music") forSegment:2];
    [_segmentedTitleControl setLabel:_NS("Local Network") forSegment:3];
    [_segmentedTitleControl setLabel:_NS("Internet") forSegment:4];
186
    [_segmentedTitleControl sizeToFit];
187
    [_segmentedTitleControl setSelectedSegment:0];
188

189
    _playlistDragDropView.dropTarget = self;
190 191 192 193
    _playlistCounterTextField.useStrongRounding = YES;
    _playlistCounterTextField.font = [NSFont VLCplaylistSelectedItemLabelFont];
    _playlistCounterTextField.textColor = [NSColor VLClibraryAnnotationColor];
    _playlistCounterTextField.hidden = YES;
194

195
    _playlistDataSource = [[VLCPlaylistDataSource alloc] init];
196
    _playlistDataSource.playlistController = _playlistController;
197
    _playlistDataSource.tableView = _playlistTableView;
198
    _playlistDataSource.dragDropView = _playlistDragDropView;
199
    _playlistDataSource.counterTextField = _playlistCounterTextField;
200
    _playlistController.playlistDataSource = _playlistDataSource;
201 202 203

    _playlistTableView.dataSource = _playlistDataSource;
    _playlistTableView.delegate = _playlistDataSource;
204
    [self updateViewCellDimensionsBasedOnSetting:nil];
205
    [_playlistTableView reloadData];
206

207 208 209 210 211 212
    _libraryVideoDataSource = [[VLCLibraryVideoDataSource alloc] init];
    _libraryVideoDataSource.libraryModel = mainInstance.libraryController.libraryModel;
    _libraryVideoDataSource.recentMediaCollectionView = _recentVideoLibraryCollectionView;
    _libraryVideoDataSource.libraryMediaCollectionView = _videoLibraryCollectionView;
    _videoLibraryCollectionView.dataSource = _libraryVideoDataSource;
    _videoLibraryCollectionView.delegate = _libraryVideoDataSource;
213 214
    [_videoLibraryCollectionView registerClass:[VLCLibraryCollectionViewItem class] forItemWithIdentifier:VLCLibraryCellIdentifier];
    [_videoLibraryCollectionView registerClass:[VLCLibraryCollectionViewSupplementaryElementView class]
215 216
               forSupplementaryViewOfKind:NSCollectionElementKindSectionHeader
                           withIdentifier:VLCLibrarySupplementaryElementViewIdentifier];
217
    [(NSCollectionViewFlowLayout *)_videoLibraryCollectionView.collectionViewLayout setHeaderReferenceSize:[VLCLibraryCollectionViewSupplementaryElementView defaultHeaderSize]];
218 219
    _recentVideoLibraryCollectionView.dataSource = _libraryVideoDataSource;
    _recentVideoLibraryCollectionView.delegate = _libraryVideoDataSource;
220
    [_recentVideoLibraryCollectionView registerClass:[VLCLibraryCollectionViewItem class] forItemWithIdentifier:VLCLibraryCellIdentifier];
221

222 223 224 225 226 227 228
    _libraryAudioDataSource = [[VLCLibraryAudioDataSource alloc] init];
    _libraryAudioDataSource.libraryModel = mainInstance.libraryController.libraryModel;
    _libraryAudioDataSource.categorySelectionTableView = _audioCategorySelectionTableView;
    _libraryAudioDataSource.collectionSelectionTableView = _audioCollectionSelectionTableView;
    _libraryAudioDataSource.groupSelectionTableView = _audioGroupSelectionTableView;
    _audioCategorySelectionTableView.dataSource = _libraryAudioDataSource;
    _audioCategorySelectionTableView.delegate = _libraryAudioDataSource;
229
    _audioCategorySelectionTableView.rowHeight = VLCLibraryWindowSmallRowHeight;
230 231
    _audioCollectionSelectionTableView.dataSource = _libraryAudioDataSource;
    _audioCollectionSelectionTableView.delegate = _libraryAudioDataSource;
232
    _audioCollectionSelectionTableView.rowHeight = VLCLibraryWindowLargeRowHeight;
233 234 235 236 237 238
    _libraryAudioGroupDataSource = [[VLCLibraryGroupDataSource alloc] init];
    _libraryAudioDataSource.groupDataSource = _libraryAudioGroupDataSource;
    _audioGroupSelectionTableView.dataSource = _libraryAudioGroupDataSource;
    _audioGroupSelectionTableView.delegate = _libraryAudioGroupDataSource;
    _audioGroupSelectionTableView.rowHeight = 450.;

239
    _mediaSourceDataSource = [[VLCMediaSourceDataSource alloc] init];
240
    _mediaSourceDataSource.collectionView = _mediaSourceCollectionView;
241 242 243
    _mediaSourceCollectionView.dataSource = _mediaSourceDataSource;
    _mediaSourceCollectionView.delegate = _mediaSourceDataSource;
    [_mediaSourceCollectionView registerClass:[VLCMediaSourceCollectionViewItem class] forItemWithIdentifier:VLCMediaSourceCellIdentifier];
244

245
    self.upNextLabel.font = [NSFont VLClibrarySectionHeaderFont];
246
    self.upNextLabel.stringValue = _NS("Playlist");
247
    [self updateColorsBasedOnAppearance];
248
    self.openMediaButton.title = _NS("Open media...");
249

250 251 252 253 254 255
    _alternativeAudioViewController = [[VLCLibraryAlternativeAudioViewController alloc] init];
    _alternativeAudioViewController.collectionView = self.alternativeAudioCollectionView;
    _alternativeAudioViewController.segmentedControl = self.alternativeAudioSegmentedControl;
    _alternativeAudioViewController.libraryModel = mainInstance.libraryController.libraryModel;
    [_alternativeAudioViewController setupAppearance];

256 257 258
    _mainSplitView.delegate = self;
    _lastPlaylistWidthBeforeCollaps = VLCLibraryWindowDefaultPlaylistWidth;

259
    [self segmentedControlAction:nil];
260 261
    [self repeatStateUpdated:nil];
    [self shuffleStateUpdated:nil];
262 263
}

264 265 266
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
267 268 269
    if (@available(macOS 10_14, *)) {
        [[NSApplication sharedApplication] removeObserver:self forKeyPath:@"effectiveAppearance"];
    }
270 271 272 273

    libvlc_int_t *libvlc = vlc_object_instance(getIntf());
    var_DelCallback(libvlc, "intf-toggle-fscontrol", ShowFullscreenController, (__bridge void *)self);
    var_DelCallback(libvlc, "intf-show", ShowController, (__bridge void *)self);
274 275
}

276 277
#pragma mark - appearance setters

278 279 280 281 282 283 284 285 286 287
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context
{
    [self updateColorsBasedOnAppearance];
}

- (void)updateColorsBasedOnAppearance
{
288 289 290 291
    if (self.contentView.shouldShowDarkAppearance) {
        self.upNextLabel.textColor = [NSColor VLClibraryDarkTitleColor];
        self.upNextSeparator.borderColor = [NSColor VLClibrarySeparatorDarkColor];
        self.clearPlaylistSeparator.borderColor = [NSColor VLClibrarySeparatorDarkColor];
292 293 294 295 296
    } else {
        self.upNextLabel.textColor = [NSColor VLClibraryLightTitleColor];
        self.upNextSeparator.borderColor = [NSColor VLClibrarySeparatorLightColor];
        self.clearPlaylistSeparator.borderColor = [NSColor VLClibrarySeparatorLightColor];
    }
297 298
}

299 300 301 302 303
- (void)updateViewCellDimensionsBasedOnSetting:(NSNotification *)aNotification
{
    _playlistTableView.rowHeight = config_GetInt("macosx-large-text") ? VLCLibraryWindowLargePlaylistRowHeight : VLCLibraryWindowSmallPlaylistRowHeight;
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
#pragma mark - playmode state display and interaction

- (IBAction)shuffleAction:(id)sender
{
    if (_playlistController.playbackOrder == VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL) {
        _playlistController.playbackOrder = VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
    } else {
        _playlistController.playbackOrder = VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL;
    }
}

- (void)shuffleStateUpdated:(NSNotification *)aNotification
{
    if (_playlistController.playbackOrder == VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL) {
        self.shufflePlaylistButton.image = [NSImage imageNamed:@"shuffleOff"];
    } else {
        self.shufflePlaylistButton.image = [NSImage imageNamed:@"shuffleOn"];
    }
}

- (IBAction)repeatAction:(id)sender
{
    enum vlc_playlist_playback_repeat currentRepeatState = _playlistController.playbackRepeat;
    switch (currentRepeatState) {
        case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL:
            _playlistController.playbackRepeat = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
            break;
        case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT:
            _playlistController.playbackRepeat = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
            break;

        default:
            _playlistController.playbackRepeat = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT;
            break;
    }
}

- (void)repeatStateUpdated:(NSNotification *)aNotification
{
    enum vlc_playlist_playback_repeat currentRepeatState = _playlistController.playbackRepeat;
    switch (currentRepeatState) {
        case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL:
            self.repeatPlaylistButton.image = [NSImage imageNamed:@"repeatAll"];
            break;
        case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT:
            self.repeatPlaylistButton.image = [NSImage imageNamed:@"repeatOne"];
            break;

        default:
            self.repeatPlaylistButton.image = [NSImage imageNamed:@"repeatOff"];
            break;
    }
}

#pragma mark - misc. user interactions

360
- (void)segmentedControlAction:(id)sender
361
{
362 363
    switch (_segmentedTitleControl.selectedSegment) {
        case 0:
364 365 366
            if (_mediaSourceScrollView.superview != nil) {
                [_mediaSourceScrollView removeFromSuperview];
            }
367 368 369
            if (_audioLibrarySplitView.superview != nil) {
                [_audioLibrarySplitView removeFromSuperview];
            }
370 371 372
            if (_alternativeAudioView.superview != nil) {
                [_alternativeAudioView removeFromSuperview];
            }
373 374 375 376 377 378 379
            if (_videoLibraryStackView.superview == nil) {
                _videoLibraryStackView.translatesAutoresizingMaskIntoConstraints = NO;
                [_libraryTargetView addSubview:_videoLibraryStackView];
                NSDictionary *dict = NSDictionaryOfVariableBindings(_videoLibraryStackView);
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_videoLibraryStackView(>=572.)]|" options:0 metrics:0 views:dict]];
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_videoLibraryStackView(>=444.)]|" options:0 metrics:0 views:dict]];
            }
380 381
            [_videoLibraryCollectionView reloadData];
            [_recentVideoLibraryCollectionView reloadData];
382 383 384
            break;

        case 1:
385 386 387
            if (_mediaSourceScrollView.superview != nil) {
                [_mediaSourceScrollView removeFromSuperview];
            }
388 389
            if (_videoLibraryStackView.superview != nil) {
                [_videoLibraryStackView removeFromSuperview];
390
            }
391 392 393
            if (_alternativeAudioView.superview != nil) {
                [_alternativeAudioView removeFromSuperview];
            }
394 395 396 397 398 399 400 401 402
            if (_audioLibrarySplitView.superview == nil) {
                _audioLibrarySplitView.translatesAutoresizingMaskIntoConstraints = NO;
                [_libraryTargetView addSubview:_audioLibrarySplitView];
                NSDictionary *dict = NSDictionaryOfVariableBindings(_audioLibrarySplitView);
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_audioLibrarySplitView(>=572.)]|" options:0 metrics:0 views:dict]];
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_audioLibrarySplitView(>=444.)]|" options:0 metrics:0 views:dict]];
            }
            [_audioCategorySelectionTableView reloadData];
            [_audioCollectionSelectionTableView reloadData];
403 404
            break;

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        case 2:
            if (_mediaSourceScrollView.superview != nil) {
                [_mediaSourceScrollView removeFromSuperview];
            }
            if (_videoLibraryStackView.superview != nil) {
                [_videoLibraryStackView removeFromSuperview];
            }
            if (_audioLibrarySplitView.superview != nil) {
                [_audioLibrarySplitView removeFromSuperview];
            }
            if (_alternativeAudioView.superview == nil) {
                _alternativeAudioView.translatesAutoresizingMaskIntoConstraints = NO;
                [_libraryTargetView addSubview:_alternativeAudioView];
                NSDictionary *dict = NSDictionaryOfVariableBindings(_alternativeAudioView);
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_alternativeAudioView(>=572.)]|" options:0 metrics:0 views:dict]];
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_alternativeAudioView(>=444.)]|" options:0 metrics:0 views:dict]];
            }
            [_alternativeAudioViewController reloadAppearance];
            break;

425
        default:
426 427
            if (_videoLibraryStackView.superview != nil) {
                [_videoLibraryStackView removeFromSuperview];
428 429 430
            }
            if (_audioLibrarySplitView.superview != nil) {
                [_audioLibrarySplitView removeFromSuperview];
431
            }
432 433 434
            if (_alternativeAudioView.superview != nil) {
                [_alternativeAudioView removeFromSuperview];
            }
435 436 437 438 439 440 441
            if (_mediaSourceScrollView.superview == nil) {
                _mediaSourceScrollView.translatesAutoresizingMaskIntoConstraints = NO;
                [_libraryTargetView addSubview:_mediaSourceScrollView];
                NSDictionary *dict = NSDictionaryOfVariableBindings(_mediaSourceScrollView);
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_mediaSourceScrollView(>=572.)]|" options:0 metrics:0 views:dict]];
                [_libraryTargetView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_mediaSourceScrollView(>=444.)]|" options:0 metrics:0 views:dict]];
            }
442
            [_mediaSourceDataSource loadMediaSources];
443
            [_mediaSourceCollectionView reloadData];
444 445
            break;
    }
446 447
}

448
- (IBAction)playlistDoubleClickAction:(id)sender
449 450 451 452 453 454 455 456
{
    NSInteger selectedRow = self.playlistTableView.selectedRow;
    if (selectedRow == -1)
        return;

    [[[VLCMain sharedInstance] playlistController] playItemAtIndex:selectedRow];
}

457 458 459 460 461
- (IBAction)clearPlaylist:(id)sender
{
    [_playlistController clearPlaylist];
}

462 463 464 465 466 467 468 469
- (IBAction)sortPlaylist:(id)sender
{
    if (!_playlistSortingMenuController) {
        _playlistSortingMenuController = [[VLCPlaylistSortingMenuController alloc] init];
    }
    [NSMenu popUpContextMenu:_playlistSortingMenuController.playlistSortingMenu withEvent:[NSApp currentEvent] forView:sender];
}

470 471 472 473 474 475 476 477
- (IBAction)sortLibrary:(id)sender
{
    if (!_librarySortingMenuController) {
        _librarySortingMenuController = [[VLCLibrarySortingMenuController alloc] init];
    }
    [NSMenu popUpContextMenu:_librarySortingMenuController.librarySortingMenu withEvent:[NSApp currentEvent] forView:sender];
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
- (IBAction)openMedia:(id)sender
{
    [[[VLCMain sharedInstance] open] openFileGeneric];
}

- (BOOL)handlePasteBoardFromDragSession:(NSPasteboard *)paste
{
    id propertyList = [paste propertyListForType:NSFilenamesPboardType];
    if (propertyList == nil) {
        return NO;
    }

    NSArray *values = [propertyList sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    NSUInteger valueCount = [values count];
    if (valueCount > 0) {
        NSMutableArray *metadataArray = [NSMutableArray arrayWithCapacity:valueCount];

        for (NSUInteger i = 0; i < valueCount; i++) {
            VLCOpenInputMetadata *inputMetadata;
            char *psz_uri = vlc_path2uri([values[i] UTF8String], "file");
            if (!psz_uri)
                continue;
            inputMetadata = [[VLCOpenInputMetadata alloc] init];
            inputMetadata.MRLString = toNSStr(psz_uri);
            free(psz_uri);
            [metadataArray addObject:inputMetadata];
        }
        [_playlistController addPlaylistItems:metadataArray];

        return YES;
    }

    return NO;
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
#pragma mark - split view delegation

- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
{
    switch (dividerIndex) {
        case 0:
            return VLCLibraryWindowMinimalWidth;
            break;

        case 1:
            return VLCLibraryWindowDefaultPlaylistWidth;
            break;

        default:
            break;
    }

    return proposedMinimumPosition;
}

- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
{
    return [subview isEqual:_playlistView];
}

- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex
{
    return [subview isEqual:_playlistView];
}

- (void)splitViewDidResizeSubviews:(NSNotification *)notification
{
    _lastPlaylistWidthBeforeCollaps = [_playlistView frame].size.width;
}

- (void)togglePlaylist
{
    [_mainSplitView adjustSubviews];
    CGFloat splitViewWidth = _mainSplitView.frame.size.width;
    if ([_mainSplitView isSubviewCollapsed:_playlistView]) {
        [_mainSplitView setPosition:splitViewWidth - _lastPlaylistWidthBeforeCollaps ofDividerAtIndex:0];
    } else {
        [_mainSplitView setPosition:splitViewWidth ofDividerAtIndex:0];
    }
}

- (IBAction)showAndHidePlaylist:(id)sender
{
    [self togglePlaylist];
}

564 565
#pragma mark - video output controlling

566
- (void)videoPlaybackWillBeStarted
567 568 569 570 571
{
    if (!self.fullscreen)
        _windowFrameBeforePlayback = [self frame];
}

572
- (void)enableVideoPlaybackAppearance
573
{
574
    [self.videoView setHidden:NO];
575

576 577 578 579 580 581 582
    if (self.nativeFullscreenMode) {
        if ([self hasActiveVideo] && [self fullscreen]) {
            [self hideControlsBar];
            [_fspanel shouldBecomeActive:nil];
        }
    }
}
583

584 585 586 587 588 589 590 591
- (void)disableVideoPlaybackAppearance
{
    if (!self.nonembedded
        && (!self.nativeFullscreenMode || (self.nativeFullscreenMode && !self.fullscreen))
        && _windowFrameBeforePlayback.size.width > 0
        && _windowFrameBeforePlayback.size.height > 0) {

        // only resize back to minimum view of this is still desired final state
592
        CGFloat f_threshold_height = VLCVideoWindowCommonMinimalHeight + [self.controlsBar height];
593 594 595 596 597
        if (_windowFrameBeforePlayback.size.height > f_threshold_height) {
            if ([[VLCMain sharedInstance] isTerminating]) {
                [self setFrame:_windowFrameBeforePlayback display:YES];
            } else {
                [[self animator] setFrame:_windowFrameBeforePlayback display:YES];
598 599
            }
        }
600
    }
601

602
    _windowFrameBeforePlayback = NSMakeRect(0, 0, 0, 0);
603

604 605
    [self makeFirstResponder: _playlistTableView];
    [[[VLCMain sharedInstance] voutProvider] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
606

607 608 609
    // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
    [self setAlphaValue:1.0];
    [self.videoView setHidden:YES];
610 611

    if (self.nativeFullscreenMode) {
612 613
        [self showControlsBar];
        [_fspanel shouldBecomeInactive:nil];
614
    }
615 616 617 618 619
}

#pragma mark - library representation and interaction
- (void)updateLibraryRepresentation:(NSNotification *)aNotification
{
620 621
    [_videoLibraryCollectionView reloadData];
    [_recentVideoLibraryCollectionView reloadData];
622 623 624
}

#pragma mark -
625 626 627 628 629 630
#pragma mark respond to core events

- (void)shouldShowController:(NSNotification *)aNotification
{
    [self makeKeyAndOrderFront:nil];
}
631

632
- (void)shouldShowFullscreenController:(NSNotification *)aNotification
633 634 635 636
{
    id currentWindow = [NSApp keyWindow];
    if ([currentWindow respondsToSelector:@selector(hasActiveVideo)] && [currentWindow hasActiveVideo]) {
        if ([currentWindow respondsToSelector:@selector(fullscreen)] && [currentWindow fullscreen] && ![[currentWindow videoView] isHidden]) {
637
            if ([_playlistController.playerController activeVideoPlayback]) {
638 639
                [_fspanel fadeIn];
            }
640 641 642 643 644
        }
    }

}

645 646 647 648 649 650 651 652 653 654 655 656
@end

@implementation VLCLibraryWindowController

- (instancetype)initWithLibraryWindow
{
    self = [super initWithWindowNibName:@"VLCLibraryWindow"];
    return self;
}

- (void)windowDidLoad
{
657
    VLCLibraryWindow *window = (VLCLibraryWindow *)self.window;
658 659 660
    [window setRestorable:NO];
    [window setExcludedFromWindowsMenu:YES];
    [window setAcceptsMouseMovedEvents:YES];
661
    [window setContentMinSize:NSMakeSize(VLCLibraryWindowMinimalWidth, VLCLibraryWindowMinimalHeight)];
662 663 664
}

@end