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.
1013 lines
31 KiB
1013 lines
31 KiB
/*****************************************************************************
|
|
* misc.m: code not specific to vlc
|
|
*****************************************************************************
|
|
* Copyright (C) 2003-2015 VLC authors and VideoLAN
|
|
* $Id$
|
|
*
|
|
* Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
|
|
* Felix Paul Kühne <fkuehne at 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 "CompatibilityFixes.h"
|
|
#import "misc.h"
|
|
#import "intf.h" /* VLCApplication */
|
|
#import "MainWindow.h"
|
|
#import "MainMenu.h"
|
|
#import "ControlsBar.h"
|
|
#import "CoreInteraction.h"
|
|
#import <CoreAudio/CoreAudio.h>
|
|
#import <vlc_keys.h>
|
|
|
|
NSString *const VLCOpenTextFieldWasClicked = @"VLCOpenTextFieldWasClicked";
|
|
|
|
/*****************************************************************************
|
|
* NSSound (VLCAdditions)
|
|
*
|
|
* added code to change the system volume, needed for the apple remote code
|
|
* this is simplified code, which won't let you set the exact volume
|
|
* (that's what the audio output is for after all), but just the system volume
|
|
* in steps of 1/16 (matching the default AR or volume key implementation).
|
|
*****************************************************************************/
|
|
|
|
@implementation NSSound (VLCAdditions)
|
|
|
|
+ (float)systemVolumeForChannel:(int)channel
|
|
{
|
|
AudioDeviceID i_device;
|
|
float f_volume;
|
|
OSStatus err;
|
|
UInt32 i_size;
|
|
|
|
i_size = sizeof( i_device );
|
|
AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
|
|
err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
|
|
if (err != noErr) {
|
|
msg_Warn( VLCIntf, "couldn't get main audio output device" );
|
|
return .0;
|
|
}
|
|
|
|
AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, channel };
|
|
i_size = sizeof( f_volume );
|
|
err = AudioObjectGetPropertyData(i_device, &propertyAddress, 0, NULL, &i_size, &f_volume);
|
|
if (err != noErr) {
|
|
msg_Warn( VLCIntf, "couldn't get volume value" );
|
|
return .0;
|
|
}
|
|
|
|
return f_volume;
|
|
}
|
|
|
|
+ (bool)setSystemVolume:(float)f_volume forChannel:(int)i_channel
|
|
{
|
|
/* the following code will fail on S/PDIF devices. there is an easy work-around, but we'd like to match the OS behavior */
|
|
|
|
AudioDeviceID i_device;
|
|
OSStatus err;
|
|
UInt32 i_size;
|
|
Boolean b_writeable;
|
|
|
|
i_size = sizeof( i_device );
|
|
AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
|
|
err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
|
|
if (err != noErr) {
|
|
msg_Warn( VLCIntf, "couldn't get main audio output device" );
|
|
return NO;
|
|
}
|
|
|
|
AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, i_channel };
|
|
i_size = sizeof( f_volume );
|
|
err = AudioObjectIsPropertySettable( i_device, &propertyAddress, &b_writeable );
|
|
if (err != noErr || !b_writeable ) {
|
|
msg_Warn( VLCIntf, "we can't set the main audio devices' volume" );
|
|
return NO;
|
|
}
|
|
err = AudioObjectSetPropertyData(i_device, &propertyAddress, 0, NULL, i_size, &f_volume);
|
|
|
|
return YES;
|
|
}
|
|
|
|
+ (void)increaseSystemVolume
|
|
{
|
|
float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
|
|
f_volume += .0625; // 1/16 to match the OS
|
|
bool b_returned = YES;
|
|
|
|
/* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
|
|
for (NSUInteger x = 1; b_returned ; x++)
|
|
b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
|
|
}
|
|
|
|
+ (void)decreaseSystemVolume
|
|
{
|
|
float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
|
|
f_volume -= .0625; // 1/16 to match the OS
|
|
bool b_returned = YES;
|
|
|
|
/* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
|
|
for (NSUInteger x = 1; b_returned ; x++)
|
|
b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* NSAnimation (VLCAdditions)
|
|
*
|
|
* Missing extension to NSAnimation
|
|
*****************************************************************************/
|
|
|
|
@implementation NSAnimation (VLCAdditions)
|
|
/* fake class attributes */
|
|
static NSMapTable *VLCAdditions_userInfo = NULL;
|
|
|
|
+ (void)load
|
|
{
|
|
/* init our fake object attribute */
|
|
VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
NSMapRemove(VLCAdditions_userInfo, (__bridge const void * __nullable)(self));
|
|
}
|
|
|
|
- (void)setUserInfo: (void *)userInfo
|
|
{
|
|
NSMapInsert(VLCAdditions_userInfo, (__bridge const void * __nullable)(self), (void*)userInfo);
|
|
}
|
|
|
|
- (void *)userInfo
|
|
{
|
|
return NSMapGet(VLCAdditions_userInfo, (__bridge const void * __nullable)(self));
|
|
}
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* NSScreen (VLCAdditions)
|
|
*
|
|
* Missing extension to NSScreen
|
|
*****************************************************************************/
|
|
|
|
@implementation NSScreen (VLCAdditions)
|
|
|
|
static NSMutableArray *blackoutWindows = NULL;
|
|
|
|
static bool b_old_spaces_style = YES;
|
|
|
|
+ (void)load
|
|
{
|
|
/* init our fake object attribute */
|
|
blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
|
|
|
|
if (OSX_MAVERICKS || OSX_YOSEMITE) {
|
|
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] init];
|
|
[userDefaults addSuiteNamed:@"com.apple.spaces"];
|
|
/* this is system settings -> mission control -> monitors using different spaces */
|
|
NSNumber *o_span_displays = [userDefaults objectForKey:@"spans-displays"];
|
|
|
|
b_old_spaces_style = [o_span_displays boolValue];
|
|
}
|
|
}
|
|
|
|
+ (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
|
|
{
|
|
NSUInteger count = [[NSScreen screens] count];
|
|
|
|
for ( NSUInteger i = 0; i < count; i++ ) {
|
|
NSScreen *screen = [[NSScreen screens] objectAtIndex:i];
|
|
if ([screen displayID] == displayID)
|
|
return screen;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)hasMenuBar
|
|
{
|
|
if (b_old_spaces_style)
|
|
return ([self displayID] == [[[NSScreen screens] firstObject] displayID]);
|
|
else
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)hasDock
|
|
{
|
|
NSRect screen_frame = [self frame];
|
|
NSRect screen_visible_frame = [self visibleFrame];
|
|
CGFloat f_menu_bar_thickness = [self hasMenuBar] ? [[NSStatusBar systemStatusBar] thickness] : 0.0;
|
|
|
|
BOOL b_found_dock = NO;
|
|
if (screen_visible_frame.size.width < screen_frame.size.width)
|
|
b_found_dock = YES;
|
|
else if (screen_visible_frame.size.height + f_menu_bar_thickness < screen_frame.size.height)
|
|
b_found_dock = YES;
|
|
|
|
return b_found_dock;
|
|
}
|
|
|
|
- (BOOL)isScreen: (NSScreen*)screen
|
|
{
|
|
return ([self displayID] == [screen displayID]);
|
|
}
|
|
|
|
- (CGDirectDisplayID)displayID
|
|
{
|
|
return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
|
|
}
|
|
|
|
- (void)blackoutOtherScreens
|
|
{
|
|
/* Free our previous blackout window (follow blackoutWindow alloc strategy) */
|
|
[blackoutWindows makeObjectsPerformSelector:@selector(close)];
|
|
[blackoutWindows removeAllObjects];
|
|
|
|
NSUInteger screenCount = [[NSScreen screens] count];
|
|
for (NSUInteger i = 0; i < screenCount; i++) {
|
|
NSScreen *screen = [[NSScreen screens] objectAtIndex:i];
|
|
VLCWindow *blackoutWindow;
|
|
NSRect screen_rect;
|
|
|
|
if ([self isScreen: screen])
|
|
continue;
|
|
|
|
screen_rect = [screen frame];
|
|
screen_rect.origin.x = screen_rect.origin.y = 0;
|
|
|
|
/* blackoutWindow alloc strategy
|
|
- The NSMutableArray blackoutWindows has the blackoutWindow references
|
|
- blackoutOtherDisplays is responsible for alloc/releasing its Windows
|
|
*/
|
|
blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
|
|
backing: NSBackingStoreBuffered defer: NO screen: screen];
|
|
[blackoutWindow setBackgroundColor:[NSColor blackColor]];
|
|
[blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
|
|
[blackoutWindow setReleasedWhenClosed:NO]; // window is released when deleted from array above
|
|
|
|
[blackoutWindow displayIfNeeded];
|
|
[blackoutWindow orderFront: self animate: YES];
|
|
|
|
[blackoutWindows addObject: blackoutWindow];
|
|
|
|
[screen setFullscreenPresentationOptions];
|
|
}
|
|
}
|
|
|
|
+ (void)unblackoutScreens
|
|
{
|
|
NSUInteger blackoutWindowCount = [blackoutWindows count];
|
|
|
|
for (NSUInteger i = 0; i < blackoutWindowCount; i++) {
|
|
VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex:i];
|
|
[[blackoutWindow screen] setNonFullscreenPresentationOptions];
|
|
[blackoutWindow closeAndAnimate: YES];
|
|
}
|
|
}
|
|
|
|
- (void)setFullscreenPresentationOptions
|
|
{
|
|
NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions];
|
|
if ([self hasMenuBar])
|
|
presentationOpts |= NSApplicationPresentationAutoHideMenuBar;
|
|
if ([self hasMenuBar] || [self hasDock])
|
|
presentationOpts |= NSApplicationPresentationAutoHideDock;
|
|
[NSApp setPresentationOptions:presentationOpts];
|
|
}
|
|
|
|
- (void)setNonFullscreenPresentationOptions
|
|
{
|
|
NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions];
|
|
if ([self hasMenuBar])
|
|
presentationOpts &= (~NSApplicationPresentationAutoHideMenuBar);
|
|
if ([self hasMenuBar] || [self hasDock])
|
|
presentationOpts &= (~NSApplicationPresentationAutoHideDock);
|
|
[NSApp setPresentationOptions:presentationOpts];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCDragDropView
|
|
*****************************************************************************/
|
|
|
|
@implementation VLCDropDisabledImageView
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[self unregisterDraggedTypes];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCDragDropView
|
|
*****************************************************************************/
|
|
|
|
@interface VLCDragDropView()
|
|
{
|
|
bool b_activeDragAndDrop;
|
|
}
|
|
@end
|
|
|
|
@implementation VLCDragDropView
|
|
|
|
- (id)initWithFrame:(NSRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self) {
|
|
// default value
|
|
[self setDrawBorder:YES];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)enablePlaylistItems
|
|
{
|
|
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
|
|
}
|
|
|
|
- (BOOL)mouseDownCanMoveWindow
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self unregisterDraggedTypes];
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
|
}
|
|
|
|
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
|
{
|
|
if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) {
|
|
b_activeDragAndDrop = YES;
|
|
[self setNeedsDisplay:YES];
|
|
|
|
return NSDragOperationCopy;
|
|
}
|
|
|
|
return NSDragOperationNone;
|
|
}
|
|
|
|
- (void)draggingEnded:(id < NSDraggingInfo >)sender
|
|
{
|
|
b_activeDragAndDrop = NO;
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)draggingExited:(id < NSDraggingInfo >)sender
|
|
{
|
|
b_activeDragAndDrop = NO;
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
|
{
|
|
BOOL b_returned;
|
|
|
|
if (_dropHandler && [_dropHandler respondsToSelector:@selector(performDragOperation:)])
|
|
b_returned = [_dropHandler performDragOperation: sender];
|
|
else // default
|
|
b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
|
|
|
|
[self setNeedsDisplay:YES];
|
|
return b_returned;
|
|
}
|
|
|
|
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
|
|
{
|
|
[self setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)dirtyRect
|
|
{
|
|
if ([self drawBorder] && b_activeDragAndDrop) {
|
|
NSRect frameRect = [self bounds];
|
|
|
|
[[NSColor selectedControlColor] set];
|
|
NSFrameRectWithWidthUsingOperation(frameRect, 2., NSCompositeSourceOver);
|
|
}
|
|
|
|
[super drawRect:dirtyRect];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
/*****************************************************************************
|
|
* MPSlider
|
|
*****************************************************************************/
|
|
@implementation MPSlider
|
|
|
|
void _drawKnobInRect(NSRect knobRect)
|
|
{
|
|
// Center knob in given rect
|
|
knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
|
|
knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
|
|
|
|
// Draw diamond
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
|
|
}
|
|
|
|
void _drawFrameInRect(NSRect frameRect)
|
|
{
|
|
// Draw frame
|
|
NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
|
|
NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)rect
|
|
{
|
|
// Draw default to make sure the slider behaves correctly
|
|
[[NSGraphicsContext currentContext] saveGraphicsState];
|
|
NSRectClip(NSZeroRect);
|
|
[super drawRect:rect];
|
|
[[NSGraphicsContext currentContext] restoreGraphicsState];
|
|
|
|
// Full size
|
|
rect = [self bounds];
|
|
int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
|
|
rect.origin.x += diff-1;
|
|
rect.origin.y += diff;
|
|
rect.size.width -= 2*diff-2;
|
|
rect.size.height -= 2*diff;
|
|
|
|
// Draw dark
|
|
NSRect knobRect = [[self cell] knobRectFlipped:NO];
|
|
[[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
|
|
_drawFrameInRect(rect);
|
|
_drawKnobInRect(knobRect);
|
|
|
|
// Draw shadow
|
|
[[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
|
|
rect.origin.x++;
|
|
rect.origin.y++;
|
|
knobRect.origin.x++;
|
|
knobRect.origin.y++;
|
|
_drawFrameInRect(rect);
|
|
_drawKnobInRect(knobRect);
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* ProgressView
|
|
*****************************************************************************/
|
|
|
|
@implementation VLCProgressView : NSView
|
|
|
|
- (void)scrollWheel:(NSEvent *)o_event
|
|
{
|
|
BOOL b_forward = NO;
|
|
CGFloat f_deltaY = [o_event deltaY];
|
|
CGFloat f_deltaX = [o_event deltaX];
|
|
|
|
if ([o_event isDirectionInvertedFromDevice])
|
|
f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
|
|
else
|
|
f_deltaY = -f_deltaY;
|
|
|
|
// positive for left / down, negative otherwise
|
|
CGFloat f_delta = f_deltaX + f_deltaY;
|
|
CGFloat f_abs;
|
|
int i_vlckey;
|
|
|
|
if (f_delta > 0.0f)
|
|
f_abs = f_delta;
|
|
else {
|
|
b_forward = YES;
|
|
f_abs = -f_delta;
|
|
}
|
|
|
|
for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
|
|
if (b_forward)
|
|
[[VLCCoreInteraction sharedInstance] forwardExtraShort];
|
|
else
|
|
[[VLCCoreInteraction sharedInstance] backwardExtraShort];
|
|
}
|
|
}
|
|
|
|
- (BOOL)acceptsFirstResponder
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* TimeLineSlider
|
|
*****************************************************************************/
|
|
|
|
@interface TimeLineSlider()
|
|
{
|
|
NSImage *o_knob_img;
|
|
NSRect img_rect;
|
|
BOOL b_dark;
|
|
}
|
|
@end
|
|
|
|
@implementation TimeLineSlider
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
|
|
o_knob_img = imageFromRes(@"progression-knob_dark");
|
|
b_dark = YES;
|
|
} else {
|
|
o_knob_img = imageFromRes(@"progression-knob");
|
|
b_dark = NO;
|
|
}
|
|
img_rect.size = [o_knob_img size];
|
|
img_rect.origin.x = img_rect.origin.y = 0;
|
|
}
|
|
|
|
- (CGFloat)knobPosition
|
|
{
|
|
NSRect knobRect = [[self cell] knobRectFlipped:NO];
|
|
knobRect.origin.x += knobRect.size.width / 2;
|
|
return knobRect.origin.x;
|
|
}
|
|
|
|
- (void)drawKnobInRect:(NSRect)knobRect
|
|
{
|
|
knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
|
|
knobRect.size.width = img_rect.size.width;
|
|
knobRect.size.height = img_rect.size.height;
|
|
[o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)rect
|
|
{
|
|
[[(VLCVideoWindowCommon *)[self window] controlsBar] drawFancyGradientEffectForTimeSlider];
|
|
msleep(10000); //wait for the gradient to draw completely
|
|
|
|
/* Draw default to make sure the slider behaves correctly */
|
|
[[NSGraphicsContext currentContext] saveGraphicsState];
|
|
NSRectClip(NSZeroRect);
|
|
[super drawRect:rect];
|
|
[[NSGraphicsContext currentContext] restoreGraphicsState];
|
|
|
|
NSRect knobRect = [[self cell] knobRectFlipped:NO];
|
|
knobRect.origin.y+=1;
|
|
[self drawKnobInRect: knobRect];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCVolumeSliderCommon
|
|
*****************************************************************************/
|
|
|
|
@implementation VLCVolumeSliderCommon : NSSlider
|
|
|
|
- (void)scrollWheel:(NSEvent *)o_event
|
|
{
|
|
BOOL b_up = NO;
|
|
CGFloat f_deltaY = [o_event deltaY];
|
|
CGFloat f_deltaX = [o_event deltaX];
|
|
|
|
if ([o_event isDirectionInvertedFromDevice])
|
|
f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
|
|
else
|
|
f_deltaY = -f_deltaY;
|
|
|
|
// positive for left / down, negative otherwise
|
|
CGFloat f_delta = f_deltaX + f_deltaY;
|
|
CGFloat f_abs;
|
|
|
|
if (f_delta > 0.0f)
|
|
f_abs = f_delta;
|
|
else {
|
|
b_up = YES;
|
|
f_abs = -f_delta;
|
|
}
|
|
|
|
for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
|
|
if (b_up)
|
|
[[VLCCoreInteraction sharedInstance] volumeUp];
|
|
else
|
|
[[VLCCoreInteraction sharedInstance] volumeDown];
|
|
}
|
|
}
|
|
|
|
- (void)drawFullVolumeMarker
|
|
{
|
|
CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
|
|
if (maxAudioVol < 1.)
|
|
return;
|
|
|
|
NSColor *drawingColor;
|
|
// for bright artwork, a black color is used and vice versa
|
|
if (_usesBrightArtwork)
|
|
drawingColor = [[NSColor blackColor] colorWithAlphaComponent:.4];
|
|
else
|
|
drawingColor = [[NSColor whiteColor] colorWithAlphaComponent:.4];
|
|
|
|
NSBezierPath* bezierPath = [NSBezierPath bezierPath];
|
|
[self drawFullVolBezierPath:bezierPath];
|
|
[bezierPath closePath];
|
|
|
|
bezierPath.lineWidth = 1.;
|
|
[drawingColor setStroke];
|
|
[bezierPath stroke];
|
|
}
|
|
|
|
- (CGFloat)fullVolumePos
|
|
{
|
|
CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
|
|
CGFloat sliderRange = [self frame].size.width - [self knobThickness];
|
|
CGFloat sliderOrigin = [self knobThickness] / 2.;
|
|
|
|
return 1. / maxAudioVol * sliderRange + sliderOrigin;
|
|
}
|
|
|
|
- (void)drawFullVolBezierPath:(NSBezierPath*)bezierPath
|
|
{
|
|
CGFloat fullVolPos = [self fullVolumePos];
|
|
[bezierPath moveToPoint:NSMakePoint(fullVolPos, [self frame].size.height - 3.)];
|
|
[bezierPath lineToPoint:NSMakePoint(fullVolPos, 2.)];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VolumeSliderCell
|
|
|
|
- (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView
|
|
{
|
|
VLCVolumeSliderCommon *o_slider = (VLCVolumeSliderCommon *)controlView;
|
|
CGFloat fullVolumePos = [o_slider fullVolumePos] + 2.;
|
|
|
|
CGPoint snapToPoint = currentPoint;
|
|
if (ABS(fullVolumePos - currentPoint.x) <= 4.)
|
|
snapToPoint.x = fullVolumePos;
|
|
|
|
return [super continueTracking:lastPoint at:snapToPoint inView:controlView];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* ITSlider
|
|
*****************************************************************************/
|
|
|
|
@interface ITSlider()
|
|
{
|
|
NSImage *img;
|
|
NSRect image_rect;
|
|
}
|
|
@end
|
|
|
|
@implementation ITSlider
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
|
|
if (b_dark)
|
|
img = imageFromRes(@"volume-slider-knob_dark");
|
|
else
|
|
img = imageFromRes(@"volume-slider-knob");
|
|
|
|
image_rect.size = [img size];
|
|
image_rect.origin.x = 0;
|
|
|
|
if (b_dark)
|
|
image_rect.origin.y = -1;
|
|
else
|
|
image_rect.origin.y = 0;
|
|
}
|
|
|
|
- (void)drawKnobInRect:(NSRect)knobRect
|
|
{
|
|
knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
|
|
knobRect.size.width = image_rect.size.width;
|
|
knobRect.size.height = image_rect.size.height;
|
|
[img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)rect
|
|
{
|
|
/* Draw default to make sure the slider behaves correctly */
|
|
[[NSGraphicsContext currentContext] saveGraphicsState];
|
|
NSRectClip(NSZeroRect);
|
|
[super drawRect:rect];
|
|
[[NSGraphicsContext currentContext] restoreGraphicsState];
|
|
|
|
[self drawFullVolumeMarker];
|
|
|
|
NSRect knobRect = [[self cell] knobRectFlipped:NO];
|
|
knobRect.origin.y+=2;
|
|
[self drawKnobInRect: knobRect];
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCTimeField implementation
|
|
*****************************************************************************
|
|
* we need this to catch our click-event in the controller window
|
|
*****************************************************************************/
|
|
|
|
@interface VLCTimeField()
|
|
{
|
|
NSShadow * o_string_shadow;
|
|
NSTextAlignment textAlignment;
|
|
|
|
NSString *o_remaining_identifier;
|
|
BOOL b_time_remaining;
|
|
}
|
|
@end
|
|
|
|
@implementation VLCTimeField
|
|
+ (void)initialize
|
|
{
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"NO", @"DisplayTimeAsTimeRemaining",
|
|
@"YES", @"DisplayFullscreenTimeAsTimeRemaining",
|
|
nil];
|
|
|
|
[defaults registerDefaults:appDefaults];
|
|
}
|
|
|
|
- (void)setRemainingIdentifier:(NSString *)o_string
|
|
{
|
|
o_remaining_identifier = o_string;
|
|
b_time_remaining = [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
|
|
}
|
|
|
|
- (void)setAlignment:(NSTextAlignment)alignment
|
|
{
|
|
textAlignment = alignment;
|
|
[self setStringValue:[self stringValue]];
|
|
}
|
|
|
|
- (void)setStringValue:(NSString *)string
|
|
{
|
|
if (!o_string_shadow) {
|
|
o_string_shadow = [[NSShadow alloc] init];
|
|
[o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
|
|
[o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
|
|
[o_string_shadow setShadowBlurRadius:0.0];
|
|
}
|
|
|
|
NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: nil];
|
|
NSUInteger i_stringLength = [string length];
|
|
|
|
[o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
|
|
[o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
|
|
[self setAttributedStringValue: o_attributed_string];
|
|
}
|
|
|
|
- (void)mouseDown: (NSEvent *)ourEvent
|
|
{
|
|
if ( [ourEvent clickCount] > 1 )
|
|
[[[VLCMain sharedInstance] mainMenu] goToSpecificTime: nil];
|
|
else
|
|
{
|
|
if (o_remaining_identifier) {
|
|
b_time_remaining = [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
|
|
b_time_remaining = !b_time_remaining;
|
|
[[NSUserDefaults standardUserDefaults] setObject:(b_time_remaining ? @"YES" : @"NO") forKey:o_remaining_identifier];
|
|
} else {
|
|
b_time_remaining = !b_time_remaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)timeRemaining
|
|
{
|
|
if (o_remaining_identifier)
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
|
|
else
|
|
return b_time_remaining;
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCMainWindowSplitView implementation
|
|
* comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
|
|
*****************************************************************************/
|
|
@implementation VLCMainWindowSplitView : NSSplitView
|
|
/* Return the color of the dividers that the split view is drawing between subviews. The default implementation of this method returns [NSColor clearColor] for the thick divider style. It will also return [NSColor clearColor] for the thin divider style when the split view is in a textured window. All other thin dividers are drawn with a color that looks good between two white panes. You can override this method to change the color of dividers.
|
|
*/
|
|
- (NSColor *)dividerColor
|
|
{
|
|
return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
|
|
}
|
|
|
|
/* Return the thickness of the dividers that the split view is drawing between subviews. The default implementation returns a value that depends on the divider style. You can override this method to change the size of dividers.
|
|
*/
|
|
- (CGFloat)dividerThickness
|
|
{
|
|
return 1.0;
|
|
}
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCThreePartImageView interface
|
|
*****************************************************************************/
|
|
|
|
@interface VLCThreePartImageView()
|
|
{
|
|
NSImage *_left_img;
|
|
NSImage *_middle_img;
|
|
NSImage *_right_img;
|
|
}
|
|
@end
|
|
|
|
@implementation VLCThreePartImageView
|
|
|
|
- (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
|
|
{
|
|
_left_img = left;
|
|
_middle_img = middle;
|
|
_right_img = right;
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)rect
|
|
{
|
|
NSRect bnds = [self bounds];
|
|
NSDrawThreePartImage( bnds, _left_img, _middle_img, _right_img, NO, NSCompositeSourceOver, 1, NO );
|
|
}
|
|
|
|
@end
|
|
|
|
@interface PositionFormatter()
|
|
{
|
|
NSCharacterSet *o_forbidden_characters;
|
|
}
|
|
@end
|
|
|
|
@implementation PositionFormatter
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
NSMutableCharacterSet *nonNumbers = [[[NSCharacterSet decimalDigitCharacterSet] invertedSet] mutableCopy];
|
|
[nonNumbers removeCharactersInString:@"-:"];
|
|
o_forbidden_characters = [nonNumbers copy];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSString*)stringForObjectValue:(id)obj
|
|
{
|
|
if([obj isKindOfClass:[NSString class]])
|
|
return obj;
|
|
if([obj isKindOfClass:[NSNumber class]])
|
|
return [obj stringValue];
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)error
|
|
{
|
|
*obj = [string copy];
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
|
|
{
|
|
if ([partialString rangeOfCharacterFromSet:o_forbidden_characters options:NSLiteralSearch].location != NSNotFound) {
|
|
return NO;
|
|
} else {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation NSView (EnableSubviews)
|
|
|
|
- (void)enableSubviews:(BOOL)b_enable
|
|
{
|
|
for (NSView *o_view in [self subviews]) {
|
|
[o_view enableSubviews:b_enable];
|
|
|
|
// enable NSControl
|
|
if ([o_view respondsToSelector:@selector(setEnabled:)]) {
|
|
[(NSControl *)o_view setEnabled:b_enable];
|
|
}
|
|
// also "enable / disable" text views
|
|
if ([o_view respondsToSelector:@selector(setTextColor:)]) {
|
|
if (b_enable == NO) {
|
|
[(NSTextField *)o_view setTextColor:[NSColor disabledControlTextColor]];
|
|
} else {
|
|
[(NSTextField *)o_view setTextColor:[NSColor controlTextColor]];
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
* VLCByteCountFormatter addition
|
|
*****************************************************************************/
|
|
|
|
@implementation VLCByteCountFormatter
|
|
|
|
+ (NSString *)stringFromByteCount:(long long)byteCount countStyle:(NSByteCountFormatterCountStyle)countStyle
|
|
{
|
|
// Use native implementation on >= mountain lion
|
|
Class byteFormatterClass = NSClassFromString(@"NSByteCountFormatter");
|
|
if (byteFormatterClass && [byteFormatterClass respondsToSelector:@selector(stringFromByteCount:countStyle:)]) {
|
|
return [byteFormatterClass stringFromByteCount:byteCount countStyle:NSByteCountFormatterCountStyleFile];
|
|
}
|
|
|
|
float devider = 0.;
|
|
float returnValue = 0.;
|
|
NSString *suffix;
|
|
|
|
NSNumberFormatter *theFormatter = [[NSNumberFormatter alloc] init];
|
|
[theFormatter setLocale:[NSLocale currentLocale]];
|
|
[theFormatter setAllowsFloats:YES];
|
|
|
|
NSString *returnString = @"";
|
|
|
|
if (countStyle != NSByteCountFormatterCountStyleDecimal)
|
|
devider = 1024.;
|
|
else
|
|
devider = 1000.;
|
|
|
|
if (byteCount < 1000) {
|
|
returnValue = byteCount;
|
|
suffix = _NS("B");
|
|
[theFormatter setMaximumFractionDigits:0];
|
|
goto end;
|
|
}
|
|
|
|
if (byteCount < 1000000) {
|
|
returnValue = byteCount / devider;
|
|
suffix = _NS("KB");
|
|
[theFormatter setMaximumFractionDigits:0];
|
|
goto end;
|
|
}
|
|
|
|
if (byteCount < 1000000000) {
|
|
returnValue = byteCount / devider / devider;
|
|
suffix = _NS("MB");
|
|
[theFormatter setMaximumFractionDigits:1];
|
|
goto end;
|
|
}
|
|
|
|
[theFormatter setMaximumFractionDigits:2];
|
|
if (byteCount < 1000000000000) {
|
|
returnValue = byteCount / devider / devider / devider;
|
|
suffix = _NS("GB");
|
|
goto end;
|
|
}
|
|
|
|
returnValue = byteCount / devider / devider / devider / devider;
|
|
suffix = _NS("TB");
|
|
|
|
end:
|
|
returnString = [NSString stringWithFormat:@"%@ %@", [theFormatter stringFromNumber:[NSNumber numberWithFloat:returnValue]], suffix];
|
|
|
|
return returnString;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation VLCOpenTextField
|
|
|
|
- (void)mouseDown:(NSEvent *)theEvent
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName: VLCOpenTextFieldWasClicked
|
|
object: self];
|
|
[super mouseDown: theEvent];
|
|
}
|
|
|
|
@end
|
|
|