/***************************************************************************** * misc.m: code not specific to vlc ***************************************************************************** * Copyright (C) 2003-2015 VLC authors and VideoLAN * $Id$ * * Authors: Jon Lech Johansen * Felix Paul Kühne * * 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 "VLCMainWindow.h" #import "VLCMainMenu.h" #import "ControlsBar.h" #import "VLCCoreInteraction.h" #import #import 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( getIntf(), "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( getIntf(), "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( getIntf(), "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( getIntf(), "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_LION && !OSX_MOUNTAIN_LION) { 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 )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 )sender { return YES; } - (BOOL)performDragOperation:(id )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 )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( getIntf(), "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( getIntf(), "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