You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1311 lines
48 KiB
1311 lines
48 KiB
/*****************************************************************************
|
|
* MainWindow.m: MacOS X interface module
|
|
*****************************************************************************
|
|
* Copyright (C) 2002-2013 VLC authors and VideoLAN
|
|
* $Id$
|
|
*
|
|
* Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
|
|
* Jon Lech Johansen <jon-vl@nanocrew.net>
|
|
* Christophe Massiot <massiot@via.ecp.fr>
|
|
* Derk-Jan Hartman <hartman at videolan.org>
|
|
* David Fuhrmann <david dot fuhrmann at googlemail dot com>
|
|
*
|
|
* 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 "CompatibilityFixes.h"
|
|
#import "MainWindow.h"
|
|
#import "intf.h"
|
|
#import "CoreInteraction.h"
|
|
#import "AudioEffects.h"
|
|
#import "MainMenu.h"
|
|
#import "open.h"
|
|
#import "controls.h" // TODO: remove me
|
|
#import "playlist.h"
|
|
#import "SideBarItem.h"
|
|
#import <math.h>
|
|
#import <vlc_playlist.h>
|
|
#import <vlc_url.h>
|
|
#import <vlc_strings.h>
|
|
#import <vlc_services_discovery.h>
|
|
|
|
#import "ControlsBar.h"
|
|
#import "VideoView.h"
|
|
#import "VLCVoutWindowController.h"
|
|
|
|
|
|
@interface VLCMainWindow (Internal)
|
|
- (void)resizePlaylistAfterCollapse;
|
|
- (void)makeSplitViewVisible;
|
|
- (void)makeSplitViewHidden;
|
|
- (void)showPodcastControls;
|
|
- (void)hidePodcastControls;
|
|
@end
|
|
|
|
|
|
@implementation VLCMainWindow
|
|
|
|
@synthesize nativeFullscreenMode=b_nativeFullscreenMode;
|
|
@synthesize nonembedded=b_nonembedded;
|
|
@synthesize fsPanel=o_fspanel;
|
|
|
|
static VLCMainWindow *_o_sharedInstance = nil;
|
|
|
|
+ (VLCMainWindow *)sharedInstance
|
|
{
|
|
return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Initialization
|
|
|
|
- (id)init
|
|
{
|
|
if (_o_sharedInstance) {
|
|
[self dealloc];
|
|
return _o_sharedInstance;
|
|
} else
|
|
_o_sharedInstance = [super init];
|
|
|
|
return _o_sharedInstance;
|
|
}
|
|
|
|
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
|
|
backing:(NSBackingStoreType)backingType defer:(BOOL)flag
|
|
{
|
|
self = [super initWithContentRect:contentRect styleMask:styleMask
|
|
backing:backingType defer:flag];
|
|
_o_sharedInstance = self;
|
|
|
|
[[VLCMain sharedInstance] updateTogglePlaylistState];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
|
|
{
|
|
char *key;
|
|
NSString *o_key;
|
|
|
|
key = config_GetPsz(VLCIntf, keyString);
|
|
o_key = [NSString stringWithFormat:@"%s", key];
|
|
FREENULL(key);
|
|
|
|
unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
|
|
|
|
NSString * characters = [o_event charactersIgnoringModifiers];
|
|
if ([characters length] > 0) {
|
|
return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
|
|
(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;
|
|
}
|
|
|
|
- (BOOL)performKeyEquivalent:(NSEvent *)o_event
|
|
{
|
|
BOOL b_force = NO;
|
|
// these are key events which should be handled by vlc core, but are attached to a main menu item
|
|
if (![self isEvent: o_event forKey: "key-vol-up"] &&
|
|
![self isEvent: o_event forKey: "key-vol-down"] &&
|
|
![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"]) {
|
|
/* We indeed want to prioritize some Cocoa key equivalent against libvlc,
|
|
so we perform the menu equivalent now. */
|
|
if ([[NSApp mainMenu] performKeyEquivalent:o_event])
|
|
return TRUE;
|
|
}
|
|
else
|
|
b_force = YES;
|
|
|
|
return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
|
|
[(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
if (b_dark_interface)
|
|
[o_color_backdrop release];
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver: self];
|
|
[o_sidebaritems release];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
// sets lion fullscreen behaviour
|
|
[super awakeFromNib];
|
|
|
|
BOOL b_splitviewShouldBeHidden = NO;
|
|
|
|
/* setup the styled interface */
|
|
b_nativeFullscreenMode = NO;
|
|
#ifdef MAC_OS_X_VERSION_10_7
|
|
if (!OSX_SNOW_LEOPARD)
|
|
b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
|
|
#endif
|
|
[self useOptimizedDrawing: YES];
|
|
|
|
[[o_search_fld cell] setPlaceholderString: _NS("Search")];
|
|
[[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
|
|
|
|
[o_dropzone_btn setTitle: _NS("Open media...")];
|
|
[[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];
|
|
[o_dropzone_lbl setStringValue: _NS("Drop media here")];
|
|
|
|
[o_podcast_add_btn setTitle: _NS("Subscribe")];
|
|
[o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
|
|
[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")];
|
|
[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")];
|
|
|
|
/* interface builder action */
|
|
float f_threshold_height = f_min_video_height + [[o_controls_bar bottomBarView] frame].size.height;
|
|
if (b_dark_interface)
|
|
f_threshold_height += [o_titlebar_view frame].size.height;
|
|
if ([[self contentView] frame].size.height < f_threshold_height)
|
|
b_splitviewShouldBeHidden = YES;
|
|
|
|
[self setDelegate: self];
|
|
[self setExcludedFromWindowsMenu: YES];
|
|
[self setAcceptsMouseMovedEvents: YES];
|
|
// Set that here as IB seems to be buggy
|
|
if (b_dark_interface)
|
|
[self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
|
|
else
|
|
[self setContentMinSize:NSMakeSize(604., 288.)];
|
|
|
|
[self setTitle: _NS("VLC media player")];
|
|
|
|
b_dropzone_active = YES;
|
|
[o_dropzone_view setFrame: [o_playlist_table frame]];
|
|
[o_left_split_view setFrame: [o_sidebar_view frame]];
|
|
|
|
if (!OSX_SNOW_LEOPARD) {
|
|
/* 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];
|
|
}
|
|
|
|
/* create the sidebar */
|
|
o_sidebaritems = [[NSMutableArray alloc] init];
|
|
SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
|
|
SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
|
|
[playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
|
|
SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
|
|
[medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
|
|
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;
|
|
char **ppsz_names = vlc_sd_GetNames(pl_Get(VLCIntf), &ppsz_longnames, &p_categories);
|
|
if (!ppsz_names)
|
|
msg_Err(VLCIntf, "no sd item found"); //TODO
|
|
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_longname++, p_category++) {
|
|
o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
|
|
switch (*p_category) {
|
|
case SD_CAT_INTERNET:
|
|
[internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
|
|
if (!strncmp(*ppsz_name, "podcast", 7))
|
|
[[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
|
|
else
|
|
[[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
|
|
[[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
|
|
[[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
|
|
break;
|
|
case SD_CAT_DEVICES:
|
|
[devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
|
|
[[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
|
|
[[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
|
|
[[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
|
|
break;
|
|
case SD_CAT_LAN:
|
|
[lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
|
|
[[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
|
|
[[lanItems lastObject] setSdtype: SD_CAT_LAN];
|
|
[[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
|
|
break;
|
|
case SD_CAT_MYCOMPUTER:
|
|
[mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
|
|
if (!strncmp(*ppsz_name, "video_dir", 9))
|
|
[[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
|
|
else if (!strncmp(*ppsz_name, "audio_dir", 9))
|
|
[[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
|
|
else if (!strncmp(*ppsz_name, "picture_dir", 11))
|
|
[[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
|
|
else
|
|
[[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
|
|
[[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
|
|
[[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
|
|
break;
|
|
default:
|
|
msg_Warn(VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name);
|
|
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]];
|
|
[mycompItems release];
|
|
[devicesItems release];
|
|
[lanItems release];
|
|
[internetItems release];
|
|
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];
|
|
|
|
[o_sidebar_view reloadData];
|
|
[o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
|
|
[o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
|
|
[o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
|
|
|
|
[o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
|
|
[(PXSourceList *)o_sidebar_view setDataSource:self];
|
|
[o_sidebar_view setDelegate:self];
|
|
[o_sidebar_view setAutosaveExpandedItems:YES];
|
|
|
|
[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];
|
|
if (![defaults objectForKey:@"VLCFirstRun"]) {
|
|
[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];
|
|
|
|
[o_fspanel center];
|
|
}
|
|
|
|
if (b_dark_interface) {
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
|
|
|
|
[self setBackgroundColor: [NSColor clearColor]];
|
|
[self setOpaque: NO];
|
|
[self display];
|
|
[self setHasShadow:NO];
|
|
[self setHasShadow:YES];
|
|
|
|
NSRect winrect = [self frame];
|
|
CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
|
|
|
|
[o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
|
|
winrect.size.width, f_titleBarHeight)];
|
|
[[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
|
|
|
|
if (winrect.size.height > 100) {
|
|
[self setFrame: winrect display:YES animate:YES];
|
|
previousSavedFrame = winrect;
|
|
}
|
|
|
|
winrect = [o_split_view frame];
|
|
winrect.size.height = winrect.size.height - f_titleBarHeight;
|
|
[o_split_view setFrame: winrect];
|
|
[o_video_view setFrame: winrect];
|
|
|
|
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];
|
|
} else {
|
|
[o_video_view setFrame: [o_split_view frame]];
|
|
[o_playlist_table setBorderType: NSNoBorder];
|
|
[o_sidebar_scrollview setBorderType: NSNoBorder];
|
|
}
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(mainSplitViewDidResizeSubviews:) name: NSSplitViewDidResizeSubviewsNotification object:o_split_view];
|
|
|
|
if (b_splitviewShouldBeHidden) {
|
|
[self hideSplitView];
|
|
i_lastSplitViewHeight = 300;
|
|
}
|
|
|
|
/* sanity check for the window size */
|
|
NSRect frame = [self frame];
|
|
NSSize screenSize = [[self screen] frame].size;
|
|
if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
|
|
nativeVideoSize = screenSize;
|
|
[self resizeWindow];
|
|
}
|
|
|
|
/* update fs button to reflect state for next startup */
|
|
if (var_InheritBool(pl_Get(VLCIntf), "fullscreen"))
|
|
[o_controls_bar setFullscreenState:YES];
|
|
|
|
/* restore split view */
|
|
i_lastLeftSplitViewWidth = 200;
|
|
/* trick NSSplitView implementation, which pretends to know better than us */
|
|
if (!config_GetInt(VLCIntf, "macosx-show-sidebar"))
|
|
[self performSelector:@selector(toggleLeftSubSplitView) withObject:nil afterDelay:0.05];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark appearance management
|
|
|
|
- (VLCMainWindowControlsBar *)controlsBar;
|
|
{
|
|
return (VLCMainWindowControlsBar *)o_controls_bar;
|
|
}
|
|
|
|
- (void)resizePlaylistAfterCollapse
|
|
{
|
|
NSRect plrect;
|
|
plrect = [o_playlist_table frame];
|
|
plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
|
|
[[o_playlist_table animator] setFrame: plrect];
|
|
|
|
NSRect rightSplitRect;
|
|
rightSplitRect = [o_right_split_view frame];
|
|
plrect = [o_dropzone_box frame];
|
|
plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
|
|
plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
|
|
[[o_dropzone_box animator] setFrame: plrect];
|
|
}
|
|
|
|
- (void)makeSplitViewVisible
|
|
{
|
|
if (b_dark_interface)
|
|
[self setContentMinSize: NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
|
|
else
|
|
[self setContentMinSize: NSMakeSize(604., 288.)];
|
|
|
|
NSRect old_frame = [self frame];
|
|
float newHeight = [self minSize].height;
|
|
if (old_frame.size.height < newHeight) {
|
|
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];
|
|
if ([self fullscreen]) {
|
|
[[o_controls_bar bottomBarView] setHidden: NO];
|
|
[o_fspanel setNonActive:nil];
|
|
}
|
|
|
|
[self makeFirstResponder: nil];
|
|
|
|
}
|
|
|
|
- (void)makeSplitViewHidden
|
|
{
|
|
if (b_dark_interface)
|
|
[self setContentMinSize: NSMakeSize(604., f_min_video_height + [o_titlebar_view frame].size.height)];
|
|
else
|
|
[self setContentMinSize: NSMakeSize(604., f_min_video_height)];
|
|
|
|
[o_split_view setHidden: YES];
|
|
[o_video_view setHidden: NO];
|
|
if ([self fullscreen]) {
|
|
[[o_controls_bar bottomBarView] setHidden: YES];
|
|
[o_fspanel setActive:nil];
|
|
}
|
|
|
|
if ([[o_video_view subviews] count] > 0)
|
|
[self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
|
|
}
|
|
|
|
// only exception for an controls bar button action
|
|
- (IBAction)togglePlaylist:(id)sender
|
|
{
|
|
if (![self isVisible] && sender != nil) {
|
|
[self makeKeyAndOrderFront: sender];
|
|
return;
|
|
}
|
|
|
|
BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
|
|
BOOL b_restored = NO;
|
|
|
|
BOOL b_have_alt_key = ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0;
|
|
if (sender && [sender isKindOfClass: [NSMenuItem class]])
|
|
b_have_alt_key = NO;
|
|
|
|
if (b_dropzone_active && b_have_alt_key) {
|
|
[self hideDropZone];
|
|
return;
|
|
}
|
|
|
|
if (!(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ((b_have_alt_key && b_activeVideo)
|
|
|| (b_nonembedded && sender != nil)
|
|
|| (!b_activeVideo && sender != nil)
|
|
|| b_minimized_view))
|
|
[self hideSplitView];
|
|
else {
|
|
if (b_splitview_removed) {
|
|
if (!b_nonembedded || (sender != nil && b_nonembedded))
|
|
[self showSplitView];
|
|
|
|
if (sender == nil)
|
|
b_minimized_view = YES;
|
|
else
|
|
b_minimized_view = NO;
|
|
|
|
if (b_activeVideo)
|
|
b_restored = YES;
|
|
}
|
|
|
|
if (!b_nonembedded) {
|
|
if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil))
|
|
[self makeSplitViewHidden];
|
|
else
|
|
[self makeSplitViewVisible];
|
|
} else {
|
|
[o_split_view setHidden: NO];
|
|
[o_playlist_table setHidden: NO];
|
|
[o_video_view setHidden: YES];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (IBAction)dropzoneButtonAction:(id)sender
|
|
{
|
|
[[[VLCMain sharedInstance] open] openFileGeneric];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark overwritten default functionality
|
|
|
|
- (void)windowResizedOrMoved:(NSNotification *)notification
|
|
{
|
|
[self saveFrameUsingName: [self frameAutosaveName]];
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)notification
|
|
{
|
|
config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
|
|
|
|
[self saveFrameUsingName: [self frameAutosaveName]];
|
|
}
|
|
|
|
|
|
- (void)someWindowWillClose:(NSNotification *)notification
|
|
{
|
|
id obj = [notification object];
|
|
|
|
// hasActiveVideo is defined for VLCVideoWindowCommon and subclasses
|
|
if ([obj respondsToSelector:@selector(hasActiveVideo)] && [obj hasActiveVideo]) {
|
|
if ([[VLCMain sharedInstance] activeVideoPlayback])
|
|
[[VLCCoreInteraction sharedInstance] stop];
|
|
}
|
|
}
|
|
|
|
- (void)someWindowWillMiniaturize:(NSNotification *)notification
|
|
{
|
|
if (config_GetInt(VLCIntf, "macosx-pause-minimized")) {
|
|
id obj = [notification object];
|
|
|
|
if ([obj class] == [VLCVideoWindowCommon class] || [obj class] == [VLCDetachedVideoWindow class] || ([obj class] == [VLCMainWindow class] && !b_nonembedded)) {
|
|
if ([[VLCMain sharedInstance] activeVideoPlayback])
|
|
[[VLCCoreInteraction sharedInstance] pause];
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Update interface and respond to foreign events
|
|
- (void)showDropZone
|
|
{
|
|
b_dropzone_active = YES;
|
|
[o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
|
|
[o_dropzone_view setFrame: [o_playlist_table frame]];
|
|
[[o_playlist_table animator] setHidden:YES];
|
|
}
|
|
|
|
- (void)hideDropZone
|
|
{
|
|
b_dropzone_active = NO;
|
|
[o_dropzone_view removeFromSuperview];
|
|
[[o_playlist_table animator] setHidden: NO];
|
|
}
|
|
|
|
- (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];
|
|
if (b_dark_interface) {
|
|
[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)];
|
|
} else {
|
|
[self setContentMinSize: NSMakeSize(604., [[o_controls_bar bottomBarView] frame].size.height)];
|
|
[self setContentMaxSize: NSMakeSize(FLT_MAX, [[o_controls_bar bottomBarView] frame].size.height)];
|
|
}
|
|
|
|
b_splitview_removed = YES;
|
|
}
|
|
|
|
- (void)showSplitView
|
|
{
|
|
[self updateWindow];
|
|
if (b_dark_interface)
|
|
[self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
|
|
else
|
|
[self setContentMinSize:NSMakeSize(604., 288.)];
|
|
[self setContentMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
|
|
|
|
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;
|
|
}
|
|
|
|
- (void)updateTimeSlider
|
|
{
|
|
[o_controls_bar updateTimeSlider];
|
|
[o_fspanel updatePositionAndTime];
|
|
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateTimeSlider)];
|
|
}
|
|
|
|
- (void)updateName
|
|
{
|
|
input_thread_t * p_input;
|
|
p_input = pl_CurrentInput(VLCIntf);
|
|
if (p_input) {
|
|
NSString *aString;
|
|
|
|
if (!config_GetPsz(VLCIntf, "video-title")) {
|
|
char *format = var_InheritString(VLCIntf, "input-title-format");
|
|
char *formated = str_format_meta(pl_Get(VLCIntf), format);
|
|
free(format);
|
|
aString = [NSString stringWithUTF8String:formated];
|
|
free(formated);
|
|
} else
|
|
aString = [NSString stringWithUTF8String:config_GetPsz(VLCIntf, "video-title")];
|
|
|
|
char *uri = input_item_GetURI(input_GetItem(p_input));
|
|
|
|
NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
|
|
if ([o_url isFileURL]) {
|
|
[self setRepresentedURL: o_url];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
|
|
[o_window setRepresentedURL:o_url];
|
|
}];
|
|
} else {
|
|
[self setRepresentedURL: nil];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
|
|
[o_window setRepresentedURL:nil];
|
|
}];
|
|
}
|
|
free(uri);
|
|
|
|
if ([aString isEqualToString:@""]) {
|
|
if ([o_url isFileURL])
|
|
aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
|
|
else
|
|
aString = [o_url absoluteString];
|
|
}
|
|
|
|
if ([aString length] > 0) {
|
|
[self setTitle: aString];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *o_window) {
|
|
[o_window setTitle:aString];
|
|
}];
|
|
|
|
[o_fspanel setStreamTitle: aString];
|
|
} else {
|
|
[self setTitle: _NS("VLC media player")];
|
|
[self setRepresentedURL: nil];
|
|
}
|
|
|
|
vlc_object_release(p_input);
|
|
} else {
|
|
[self setTitle: _NS("VLC media player")];
|
|
[self setRepresentedURL: nil];
|
|
}
|
|
}
|
|
|
|
- (void)updateWindow
|
|
{
|
|
[o_controls_bar updateControls];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(updateControls)];
|
|
|
|
bool b_seekable = false;
|
|
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
input_thread_t * p_input = playlist_CurrentInput(p_playlist);
|
|
if (p_input) {
|
|
/* seekable streams */
|
|
b_seekable = var_GetBool(p_input, "can-seek");
|
|
|
|
vlc_object_release(p_input);
|
|
}
|
|
|
|
[self updateTimeSlider];
|
|
if ([o_fspanel respondsToSelector:@selector(setSeekable:)])
|
|
[o_fspanel setSeekable: b_seekable];
|
|
|
|
PL_LOCK;
|
|
if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
|
|
[self hideDropZone];
|
|
else
|
|
[self showDropZone];
|
|
PL_UNLOCK;
|
|
[o_sidebar_view setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)setPause
|
|
{
|
|
[o_controls_bar setPause];
|
|
[o_fspanel setPause];
|
|
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPause)];
|
|
}
|
|
|
|
- (void)setPlay
|
|
{
|
|
[o_controls_bar setPlay];
|
|
[o_fspanel setPlay];
|
|
|
|
[[[VLCMain sharedInstance] voutController] updateWindowsControlsBarWithSelector:@selector(setPlay)];
|
|
|
|
}
|
|
|
|
- (void)updateVolumeSlider
|
|
{
|
|
[[self controlsBar] updateVolumeSlider];
|
|
[o_fspanel setVolumeLevel: [[VLCCoreInteraction sharedInstance] volume]];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Video Output handling
|
|
|
|
- (void)setVideoplayEnabled
|
|
{
|
|
BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
|
|
|
|
if (b_videoPlayback) {
|
|
if (!b_fullscreen)
|
|
frameBeforePlayback = [self frame];
|
|
} else {
|
|
if (!b_nonembedded && !b_fullscreen && frameBeforePlayback.size.width > 0 && frameBeforePlayback.size.height > 0)
|
|
[[self animator] setFrame:frameBeforePlayback display:YES];
|
|
|
|
// update fs button to reflect state for next startup
|
|
if (var_InheritBool(pl_Get(VLCIntf), "fullscreen")) {
|
|
[o_controls_bar setFullscreenState:YES];
|
|
}
|
|
|
|
[self makeFirstResponder: nil];
|
|
[[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
|
|
|
|
// restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
|
|
[self setAlphaValue:1.0];
|
|
}
|
|
|
|
if (b_nativeFullscreenMode) {
|
|
if ([self hasActiveVideo] && [self fullscreen]) {
|
|
[[o_controls_bar bottomBarView] setHidden: b_videoPlayback];
|
|
[o_fspanel setActive: nil];
|
|
} else {
|
|
[[o_controls_bar bottomBarView] setHidden: NO];
|
|
[o_fspanel setNonActive: nil];
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Lion native fullscreen handling
|
|
- (void)windowWillEnterFullScreen:(NSNotification *)notification
|
|
{
|
|
[super windowWillEnterFullScreen:notification];
|
|
|
|
// update split view frame after removing title bar
|
|
if (b_dark_interface) {
|
|
NSRect frame = [[self contentView] frame];
|
|
frame.origin.y += [o_controls_bar height];
|
|
frame.size.height -= [o_controls_bar height];
|
|
[o_split_view setFrame:frame];
|
|
}
|
|
}
|
|
|
|
- (void)windowWillExitFullScreen:(NSNotification *)notification
|
|
{
|
|
[super windowWillExitFullScreen: notification];
|
|
|
|
// update split view frame after readding title bar
|
|
if (b_dark_interface) {
|
|
NSRect frame = [o_split_view frame];
|
|
frame.size.height -= [o_titlebar_view frame].size.height;
|
|
[o_split_view setFrame:frame];
|
|
}
|
|
}
|
|
#pragma mark -
|
|
#pragma mark Fullscreen support
|
|
|
|
- (void)showFullscreenController
|
|
{
|
|
|
|
id currentWindow = [NSApp keyWindow];
|
|
if ([currentWindow respondsToSelector:@selector(hasActiveVideo)] && [currentWindow hasActiveVideo]) {
|
|
if ([currentWindow respondsToSelector:@selector(fullscreen)] && [currentWindow fullscreen] && ![[currentWindow videoView] isHidden]) {
|
|
|
|
if ([[VLCMain sharedInstance] activeVideoPlayback])
|
|
[o_fspanel fadeIn];
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
- (void)makeKeyAndOrderFront: (id)sender
|
|
{
|
|
/* Hack
|
|
* when we exit fullscreen and fade out, we may endup in
|
|
* having a window that is faded. We can't have it fade in unless we
|
|
* animate again. */
|
|
|
|
if (!b_window_is_invisible) {
|
|
/* Make sure we don't do it too much */
|
|
[super makeKeyAndOrderFront: sender];
|
|
return;
|
|
}
|
|
|
|
[super setAlphaValue:0.0f];
|
|
[super makeKeyAndOrderFront: sender];
|
|
|
|
NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
|
|
[dict setObject:self forKey:NSViewAnimationTargetKey];
|
|
[dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
|
|
|
|
o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
|
|
[dict release];
|
|
|
|
[o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
|
|
[o_makekey_anim setDuration: 0.1];
|
|
[o_makekey_anim setFrameRate: 30];
|
|
[o_makekey_anim setDelegate: self];
|
|
|
|
[o_makekey_anim startAnimation];
|
|
b_window_is_invisible = NO;
|
|
|
|
/* fullscreenAnimation will be unlocked when animation ends */
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark split view delegate
|
|
- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
|
|
{
|
|
if (dividerIndex == 0)
|
|
return 300.;
|
|
else
|
|
return proposedMax;
|
|
}
|
|
|
|
- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
|
|
{
|
|
if (dividerIndex == 0)
|
|
return 100.;
|
|
else
|
|
return proposedMin;
|
|
}
|
|
|
|
- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
|
|
{
|
|
return ([subview isEqual:o_left_split_view]);
|
|
}
|
|
|
|
- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
|
|
{
|
|
if ([subview isEqual:o_left_split_view])
|
|
return NO;
|
|
return YES;
|
|
}
|
|
|
|
- (void)mainSplitViewDidResizeSubviews:(id)object
|
|
{
|
|
i_lastLeftSplitViewWidth = [o_left_split_view frame].size.width;
|
|
config_PutInt(VLCIntf, "macosx-show-sidebar", ![o_split_view isSubviewCollapsed:o_left_split_view]);
|
|
[[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
|
|
}
|
|
|
|
- (void)toggleLeftSubSplitView
|
|
{
|
|
[o_split_view adjustSubviews];
|
|
if ([o_split_view isSubviewCollapsed:o_left_split_view])
|
|
[o_split_view setPosition:i_lastLeftSplitViewWidth ofDividerAtIndex:0];
|
|
else
|
|
[o_split_view setPosition:[o_split_view minPossiblePositionOfDividerAtIndex:0] ofDividerAtIndex:0];
|
|
[[[VLCMain sharedInstance] mainMenu] updateSidebarMenuItem];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Side Bar Data handling
|
|
/* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
|
|
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
|
|
{
|
|
//Works the same way as the NSOutlineView data source: `nil` means a parent item
|
|
if (item==nil)
|
|
return [o_sidebaritems count];
|
|
else
|
|
return [[item children] count];
|
|
}
|
|
|
|
|
|
- (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
|
|
{
|
|
//Works the same way as the NSOutlineView data source: `nil` means a parent item
|
|
if (item==nil)
|
|
return [o_sidebaritems objectAtIndex:index];
|
|
else
|
|
return [[item children] objectAtIndex:index];
|
|
}
|
|
|
|
|
|
- (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
|
|
{
|
|
return [item title];
|
|
}
|
|
|
|
- (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
|
|
{
|
|
[item setTitle:object];
|
|
}
|
|
|
|
- (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
|
|
{
|
|
return [item hasChildren];
|
|
}
|
|
|
|
|
|
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
|
|
{
|
|
if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
|
|
return YES;
|
|
|
|
return [item hasBadge];
|
|
}
|
|
|
|
|
|
- (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
|
|
{
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
NSInteger i_playlist_size;
|
|
|
|
if ([[item identifier] isEqualToString: @"playlist"]) {
|
|
PL_LOCK;
|
|
i_playlist_size = p_playlist->p_local_category->i_children;
|
|
PL_UNLOCK;
|
|
|
|
return i_playlist_size;
|
|
}
|
|
if ([[item identifier] isEqualToString: @"medialibrary"]) {
|
|
PL_LOCK;
|
|
i_playlist_size = p_playlist->p_ml_category->i_children;
|
|
PL_UNLOCK;
|
|
|
|
return i_playlist_size;
|
|
}
|
|
|
|
return [item badgeValue];
|
|
}
|
|
|
|
|
|
- (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
|
|
{
|
|
return [item hasIcon];
|
|
}
|
|
|
|
|
|
- (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
|
|
{
|
|
return [item icon];
|
|
}
|
|
|
|
- (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
|
|
{
|
|
if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
|
|
if (item != nil) {
|
|
NSMenu * m;
|
|
if ([item sdtype] > 0)
|
|
{
|
|
m = [[NSMenu alloc] init];
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
|
|
if (!sd_loaded)
|
|
[m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
|
|
else
|
|
[m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
|
|
[[m itemAtIndex:0] setRepresentedObject: [item identifier]];
|
|
}
|
|
return [m autorelease];
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (IBAction)sdmenuhandler:(id)sender
|
|
{
|
|
NSString * identifier = [sender representedObject];
|
|
if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"]) {
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [identifier UTF8String]);
|
|
|
|
if (!sd_loaded)
|
|
playlist_ServicesDiscoveryAdd(p_playlist, [identifier UTF8String]);
|
|
else
|
|
playlist_ServicesDiscoveryRemove(p_playlist, [identifier UTF8String]);
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Side Bar Delegate Methods
|
|
/* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
|
|
- (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
|
|
{
|
|
if ([[group identifier] isEqualToString:@"library"])
|
|
return YES;
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)sourceListSelectionDidChange:(NSNotification *)notification
|
|
{
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
|
|
NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
|
|
id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
|
|
|
|
|
|
//Set the label text to represent the new selection
|
|
if ([item sdtype] > -1 && [[item identifier] length] > 0) {
|
|
BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded(p_playlist, [[item identifier] UTF8String]);
|
|
if (!sd_loaded)
|
|
playlist_ServicesDiscoveryAdd(p_playlist, [[item identifier] UTF8String]);
|
|
}
|
|
|
|
[o_chosen_category_lbl setStringValue:[item title]];
|
|
|
|
if ([[item identifier] isEqualToString:@"playlist"]) {
|
|
[[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
|
|
} else if ([[item identifier] isEqualToString:@"medialibrary"]) {
|
|
[[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
|
|
} else {
|
|
playlist_item_t * pl_item;
|
|
PL_LOCK;
|
|
pl_item = playlist_ChildSearchName(p_playlist->p_root, [[item untranslatedTitle] UTF8String]);
|
|
PL_UNLOCK;
|
|
[[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
|
|
}
|
|
|
|
PL_LOCK;
|
|
if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
|
|
[self hideDropZone];
|
|
else
|
|
[self showDropZone];
|
|
PL_UNLOCK;
|
|
|
|
if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
|
|
[self showPodcastControls];
|
|
else
|
|
[self hidePodcastControls];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
|
|
object: nil
|
|
userInfo: nil];
|
|
}
|
|
|
|
- (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
|
|
{
|
|
if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"]) {
|
|
NSPasteboard *o_pasteboard = [info draggingPasteboard];
|
|
if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
|
|
return NSDragOperationGeneric;
|
|
}
|
|
return NSDragOperationNone;
|
|
}
|
|
|
|
- (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
|
|
{
|
|
NSPasteboard *o_pasteboard = [info draggingPasteboard];
|
|
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
playlist_item_t *p_node;
|
|
|
|
if ([[item identifier] isEqualToString:@"playlist"])
|
|
p_node = p_playlist->p_local_category;
|
|
else
|
|
p_node = p_playlist->p_ml_category;
|
|
|
|
if ([[o_pasteboard types] containsObject: NSFilenamesPboardType]) {
|
|
NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
|
|
NSUInteger count = [o_values count];
|
|
NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for(NSUInteger i = 0; i < count; i++) {
|
|
NSDictionary *o_dic;
|
|
char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
|
|
if (!psz_uri)
|
|
continue;
|
|
|
|
o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
|
|
|
|
free(psz_uri);
|
|
|
|
[o_array addObject: o_dic];
|
|
}
|
|
|
|
[[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
|
|
return YES;
|
|
}
|
|
else if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"]) {
|
|
NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
|
|
|
|
NSUInteger count = [array count];
|
|
playlist_item_t * p_item = NULL;
|
|
|
|
PL_LOCK;
|
|
for(NSUInteger i = 0; i < count; i++) {
|
|
p_item = [[array objectAtIndex:i] pointerValue];
|
|
if (!p_item) continue;
|
|
playlist_NodeAddCopy(p_playlist, p_item, p_node, PLAYLIST_END);
|
|
}
|
|
PL_UNLOCK;
|
|
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
|
|
{
|
|
return [item identifier];
|
|
}
|
|
|
|
- (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
|
|
{
|
|
/* the following code assumes for sakes of simplicity that only the top level
|
|
* items are allowed to have children */
|
|
|
|
NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
|
|
NSUInteger count = [array count];
|
|
if (count < 1)
|
|
return nil;
|
|
|
|
for (NSUInteger x = 0; x < count; x++) {
|
|
id item = [array objectAtIndex: x]; // save one objc selector call
|
|
if ([[item identifier] isEqualToString:object])
|
|
return item;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Podcast
|
|
|
|
- (IBAction)addPodcast:(id)sender
|
|
{
|
|
[NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
|
|
}
|
|
|
|
- (IBAction)addPodcastWindowAction:(id)sender
|
|
{
|
|
[o_podcast_subscribe_window orderOut:sender];
|
|
[NSApp endSheet: o_podcast_subscribe_window];
|
|
|
|
if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
|
|
NSMutableString * podcastConf = [[NSMutableString alloc] init];
|
|
if (config_GetPsz(VLCIntf, "podcast-urls") != NULL)
|
|
[podcastConf appendFormat:@"%s|", config_GetPsz(VLCIntf, "podcast-urls")];
|
|
|
|
[podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
|
|
config_PutPsz(VLCIntf, "podcast-urls", [podcastConf UTF8String]);
|
|
var_SetString(pl_Get(VLCIntf), "podcast-urls", [podcastConf UTF8String]);
|
|
[podcastConf release];
|
|
}
|
|
}
|
|
|
|
- (IBAction)removePodcast:(id)sender
|
|
{
|
|
if (config_GetPsz(VLCIntf, "podcast-urls") != NULL) {
|
|
[o_podcast_unsubscribe_pop removeAllItems];
|
|
[o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
|
|
[NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
|
|
}
|
|
}
|
|
|
|
- (IBAction)removePodcastWindowAction:(id)sender
|
|
{
|
|
[o_podcast_unsubscribe_window orderOut:sender];
|
|
[NSApp endSheet: o_podcast_unsubscribe_window];
|
|
|
|
if (sender == o_podcast_unsubscribe_ok_btn) {
|
|
NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz(VLCIntf, "podcast-urls")] componentsSeparatedByString:@"|"]];
|
|
[urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
|
|
config_PutPsz(VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String]);
|
|
var_SetString(pl_Get(VLCIntf), "podcast-urls", config_GetPsz(VLCIntf, "podcast-urls"));
|
|
[urls release];
|
|
|
|
/* reload the podcast module, since it won't update its list when removing podcasts */
|
|
playlist_t * p_playlist = pl_Get(VLCIntf);
|
|
if (playlist_IsServicesDiscoveryLoaded(p_playlist, "podcast{longname=\"Podcasts\"}")) {
|
|
playlist_ServicesDiscoveryRemove(p_playlist, "podcast{longname=\"Podcasts\"}");
|
|
playlist_ServicesDiscoveryAdd(p_playlist, "podcast{longname=\"Podcasts\"}");
|
|
[o_playlist_table reloadData];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
- (void)showPodcastControls
|
|
{
|
|
NSRect podcastViewDimensions = [o_podcast_view frame];
|
|
NSRect rightSplitRect = [o_right_split_view frame];
|
|
NSRect playlistTableRect = [o_playlist_table frame];
|
|
|
|
podcastViewDimensions.size.width = rightSplitRect.size.width;
|
|
podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
|
|
[o_podcast_view setFrame:podcastViewDimensions];
|
|
|
|
playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
|
|
playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
|
|
[o_playlist_table setFrame:playlistTableRect];
|
|
[o_playlist_table setNeedsDisplay:YES];
|
|
|
|
[o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
|
|
b_podcastView_displayed = YES;
|
|
}
|
|
|
|
- (void)hidePodcastControls
|
|
{
|
|
if (b_podcastView_displayed) {
|
|
NSRect podcastViewDimensions = [o_podcast_view frame];
|
|
NSRect playlistTableRect = [o_playlist_table frame];
|
|
|
|
playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
|
|
playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
|
|
|
|
[o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
|
|
[o_playlist_table setFrame: playlistTableRect];
|
|
b_podcastView_displayed = NO;
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VLCDetachedVideoWindow
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
// sets lion fullscreen behaviour
|
|
[super awakeFromNib];
|
|
[self setAcceptsMouseMovedEvents: YES];
|
|
|
|
if (b_dark_interface) {
|
|
[self setBackgroundColor: [NSColor clearColor]];
|
|
|
|
[self setOpaque: NO];
|
|
[self display];
|
|
[self setHasShadow:NO];
|
|
[self setHasShadow:YES];
|
|
|
|
NSRect winrect = [self frame];
|
|
CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
|
|
|
|
[self setTitle: _NS("VLC media player")];
|
|
[o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight)];
|
|
[[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: nil];
|
|
|
|
} else {
|
|
[self setBackgroundColor: [NSColor blackColor]];
|
|
}
|
|
|
|
NSRect videoViewRect = [[self contentView] bounds];
|
|
if (b_dark_interface)
|
|
videoViewRect.size.height -= [o_titlebar_view frame].size.height;
|
|
CGFloat f_bottomBarHeight = [[[self controlsBar] bottomBarView] frame].size.height;
|
|
videoViewRect.size.height -= f_bottomBarHeight;
|
|
videoViewRect.origin.y = f_bottomBarHeight;
|
|
[o_video_view setFrame: videoViewRect];
|
|
|
|
if (b_dark_interface) {
|
|
o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_video_view frame]];
|
|
[[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_video_view];
|
|
[o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
|
|
|
|
[self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height + [o_titlebar_view frame].size.height)];
|
|
} else {
|
|
[self setContentMinSize: NSMakeSize(363., f_min_video_height + [[[self controlsBar] bottomBarView] frame].size.height)];
|
|
}
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
if (b_dark_interface)
|
|
[o_color_backdrop release];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|