X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fmisc.m;h=b20ab77fff7d42f949b825ae1c0d0185e5438308;hb=f35c3d113308cf288376db1a6a342ed2b324f257;hp=fe57179bc6570417461c169d94bfd4e74c4c5137;hpb=c226bf881fdab1eb6344655831388ecf43686521;p=vlc diff --git a/modules/gui/macosx/misc.m b/modules/gui/macosx/misc.m index fe57179bc6..b20ab77fff 100644 --- a/modules/gui/macosx/misc.m +++ b/modules/gui/macosx/misc.m @@ -1,7 +1,7 @@ /***************************************************************************** * misc.m: code not specific to vlc ***************************************************************************** - * Copyright (C) 2003-2009 the VideoLAN team + * Copyright (C) 2003-2014 VLC authors and VideoLAN * $Id$ * * Authors: Jon Lech Johansen @@ -22,67 +22,107 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#import -#import - -#import "intf.h" /* VLCApplication */ +#import "CompatibilityFixes.h" #import "misc.h" -#import "playlist.h" +#import "intf.h" /* VLCApplication */ +#import "MainWindow.h" +#import "ControlsBar.h" #import "controls.h" +#import "CoreInteraction.h" +#import +#import + /***************************************************************************** - * NSImage (VLCAdditions) + * NSSound (VLCAdditions) * - * Addition to NSImage + * 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 NSImage (VLCAdditions) -+ (id)imageWithSystemName:(int)name -{ - /* ugly Carbon stuff following... - * regrettably, you can't get the icons through clean Cocoa */ - - /* retrieve our error icon */ - NSImage * icon; - IconRef ourIconRef; - int returnValue; - returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef); - icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease]; - [icon lockFocus]; - CGRect rect = CGRectMake(0,0,32,32); - PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext] - graphicsPort], - &rect, - kAlignNone, - kTransformNone, - NULL /*inLabelColor*/, - kPlotIconRefNormalFlags, - (IconRef)ourIconRef); - [icon unlockFocus]; - returnValue = ReleaseIconRef(ourIconRef); - return icon; -} - -+ (id)imageWithWarningIcon -{ - static NSImage * imageWithWarningIcon = nil; - if( !imageWithWarningIcon ) - { - imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain]; + +@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 imageWithWarningIcon; + + return f_volume; } -+ (id)imageWithErrorIcon ++ (bool)setSystemVolume:(float)f_volume forChannel:(int)i_channel { - static NSImage * imageWithErrorIcon = nil; - if( !imageWithErrorIcon ) - { - imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain]; + /* 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; } - return imageWithErrorIcon; + 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) * @@ -126,35 +166,57 @@ static NSMapTable *VLCAdditions_userInfo = NULL; static NSMutableArray *blackoutWindows = NULL; +static bool b_old_spaces_style = YES; + + (void)load { /* init our fake object attribute */ blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1]; -} -- (void)dealloc -{ - [blackoutWindows removeAllObjects]; - [blackoutWindows release]; - [super dealloc]; + 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]; + [userDefaults release]; + } } + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID { - int i; - - for( i = 0; i < [[NSScreen screens] count]; i++ ) - { - NSScreen *screen = [[NSScreen screens] objectAtIndex: i]; - if([screen displayID] == 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)isMainScreen +- (BOOL)hasMenuBar +{ + if (b_old_spaces_style) + return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]); + else + return YES; +} + +- (BOOL)hasDock { - return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]); + 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 @@ -164,24 +226,22 @@ static NSMutableArray *blackoutWindows = NULL; - (CGDirectDisplayID)displayID { - return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue]; + return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue]; } - (void)blackoutOtherScreens { - unsigned int i; - /* Free our previous blackout window (follow blackoutWindow alloc strategy) */ [blackoutWindows makeObjectsPerformSelector:@selector(close)]; [blackoutWindows removeAllObjects]; - for(i = 0; i < [[NSScreen screens] count]; i++) - { - NSScreen *screen = [[NSScreen screens] objectAtIndex: i]; + 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]) + + if ([self isScreen: screen]) continue; screen_rect = [screen frame]; @@ -195,268 +255,93 @@ static NSMutableArray *blackoutWindows = NULL; 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]; [blackoutWindow release]; - - if( [screen isMainScreen ] ) - SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar); + + [screen setFullscreenPresentationOptions]; } } + (void)unblackoutScreens { - unsigned int i; + NSUInteger blackoutWindowCount = [blackoutWindows count]; - for(i = 0; i < [blackoutWindows count]; i++) - { - VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i]; + for (NSUInteger i = 0; i < blackoutWindowCount; i++) { + VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex:i]; + [[blackoutWindow screen] setNonFullscreenPresentationOptions]; [blackoutWindow closeAndAnimate: YES]; } - - SetSystemUIMode( kUIModeNormal, 0); -} - -@end - -/***************************************************************************** - * VLCWindow - * - * Missing extension to NSWindow - *****************************************************************************/ - -@implementation VLCWindow -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask - backing:(NSBackingStoreType)backingType defer:(BOOL)flag -{ - self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag]; - if( self ) - b_isset_canBecomeKeyWindow = NO; - return self; -} -- (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey -{ - b_isset_canBecomeKeyWindow = YES; - b_canBecomeKeyWindow = canBecomeKey; -} - -- (BOOL)canBecomeKeyWindow -{ - if(b_isset_canBecomeKeyWindow) - return b_canBecomeKeyWindow; - - return [super canBecomeKeyWindow]; } -- (void)closeAndAnimate: (BOOL)animate +- (void)setFullscreenPresentationOptions { - NSInvocation *invoc; - - if (!animate) - { - [super close]; - return; - } - - invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]]; - [invoc setTarget: (id)super]; - - if (![self isVisible] || [self alphaValue] == 0.0) - { - [super close]; - return; - } - - [self orderOut: self animate: YES callback: invoc]; + NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions]; + if ([self hasMenuBar]) + presentationOpts |= NSApplicationPresentationAutoHideMenuBar; + if ([self hasMenuBar] || [self hasDock]) + presentationOpts |= NSApplicationPresentationAutoHideDock; + [NSApp setPresentationOptions:presentationOpts]; } -- (void)orderOut: (id)sender animate: (BOOL)animate +- (void)setNonFullscreenPresentationOptions { - NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]]; - [invoc setTarget: (id)super]; - [invoc setArgument: sender atIndex: 0]; - [self orderOut: sender animate: animate callback: invoc]; + NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions]; + if ([self hasMenuBar]) + presentationOpts &= (~NSApplicationPresentationAutoHideMenuBar); + if ([self hasMenuBar] || [self hasDock]) + presentationOpts &= (~NSApplicationPresentationAutoHideDock); + [NSApp setPresentationOptions:presentationOpts]; } -- (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback -{ - NSViewAnimation *anim; - NSViewAnimation *current_anim; - NSMutableDictionary *dict; - - if (!animate) - { - [self orderOut: sender]; - return; - } - - dict = [[NSMutableDictionary alloc] initWithCapacity:2]; - - [dict setObject:self forKey:NSViewAnimationTargetKey]; - - [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey]; - anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]]; - [dict release]; - - [anim setAnimationBlockingMode:NSAnimationNonblocking]; - [anim setDuration:0.9]; - [anim setFrameRate:30]; - [anim setUserInfo: callback]; +@end - @synchronized(self) { - current_anim = self->animation; +/***************************************************************************** + * VLCDragDropView + *****************************************************************************/ - if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating]) - { - [anim release]; - } - else - { - if (current_anim) - { - [current_anim stopAnimation]; - [anim setCurrentProgress:1.0-[current_anim currentProgress]]; - [current_anim release]; - } - else - [anim setCurrentProgress:1.0 - [self alphaValue]]; - self->animation = anim; - [self setDelegate: self]; - [anim startAnimation]; - } - } -} +@implementation VLCDropDisabledImageView -- (void)orderFront: (id)sender animate: (BOOL)animate +- (void)awakeFromNib { - NSViewAnimation *anim; - NSViewAnimation *current_anim; - NSMutableDictionary *dict; - - if (!animate) - { - [super orderFront: sender]; - [self setAlphaValue: 1.0]; - return; - } - - if (![self isVisible]) - { - [self setAlphaValue: 0.0]; - [super orderFront: sender]; - } - else if ([self alphaValue] == 1.0) - { - [super orderFront: self]; - return; - } - - dict = [[NSMutableDictionary alloc] initWithCapacity:2]; - - [dict setObject:self forKey:NSViewAnimationTargetKey]; - - [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey]; - anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]]; - [dict release]; - - [anim setAnimationBlockingMode:NSAnimationNonblocking]; - [anim setDuration:0.5]; - [anim setFrameRate:30]; - - @synchronized(self) { - current_anim = self->animation; - - if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating]) - { - [anim release]; - } - else - { - if (current_anim) - { - [current_anim stopAnimation]; - [anim setCurrentProgress:1.0 - [current_anim currentProgress]]; - [current_anim release]; - } - else - [anim setCurrentProgress:[self alphaValue]]; - self->animation = anim; - [self setDelegate: self]; - [self orderFront: sender]; - [anim startAnimation]; - } - } + [self unregisterDraggedTypes]; } -- (void)animationDidEnd:(NSAnimation*)anim -{ - if ([self alphaValue] <= 0.0) - { - NSInvocation * invoc; - [super orderOut: nil]; - [self setAlphaValue: 1.0]; - if ((invoc = [anim userInfo])) - [invoc invoke]; - } -} @end /***************************************************************************** - * VLCControllerWindow + * VLCDragDropView *****************************************************************************/ -@implementation VLCControllerWindow +@implementation VLCDragDropView -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask - backing:(NSBackingStoreType)backingType defer:(BOOL)flag -{ - /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts - * needs some further investigation! -- feepk - BOOL b_useTextured = YES; - - if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] ) - { - b_useTextured = NO; - styleMask ^= NSTexturedBackgroundWindowMask; - } */ - - self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask - backing:backingType defer:flag]; +@synthesize dropHandler=_dropHandler; +@synthesize drawBorder; - [[VLCMain sharedInstance] updateTogglePlaylistState]; - - /* FIXME: see above... - if(! b_useTextured ) - { - [self setContentBorderThickness:28.0 forEdge:NSMinYEdge]; +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + // default value + [self setDrawBorder:YES]; } - */ + return self; } -- (BOOL)performKeyEquivalent:(NSEvent *)o_event +- (void)enablePlaylistItems { - /* We indeed want to prioritize Cocoa key equivalent against libvlc, - so we perform the menu equivalent now. */ - if([[NSApp mainMenu] performKeyEquivalent:o_event]) - return TRUE; - - return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] || - [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event]; + [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]]; } -@end - - - -/***************************************************************************** - * VLCControllerView - *****************************************************************************/ - -@implementation VLCControllerView +- (BOOL)mouseDownCanMoveWindow +{ + return YES; +} - (void)dealloc { @@ -466,143 +351,66 @@ static NSMutableArray *blackoutWindows = NULL; - (void)awakeFromNib { - [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType, - NSFilenamesPboardType, nil]]; + [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; } - (NSDragOperation)draggingEntered:(id )sender { - if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) - == NSDragOperationGeneric) - { - return NSDragOperationGeneric; - } - else - { - return NSDragOperationNone; + if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) { + b_activeDragAndDrop = YES; + [self setNeedsDisplay:YES]; + + return NSDragOperationCopy; } -} -- (BOOL)prepareForDragOperation:(id )sender -{ - return YES; + return NSDragOperationNone; } -- (BOOL)performDragOperation:(id )sender +- (void)draggingEnded:(id < NSDraggingInfo >)sender { - NSPasteboard *o_paste = [sender draggingPasteboard]; - NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil]; - NSString *o_desired_type = [o_paste availableTypeFromArray:o_types]; - NSData *o_carried_data = [o_paste dataForType:o_desired_type]; - - if( o_carried_data ) - { - if ([o_desired_type isEqualToString:NSFilenamesPboardType]) - { - int i; - NSArray *o_array = [NSArray array]; - NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] - sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; - - for( i = 0; i < (int)[o_values count]; i++) - { - NSDictionary *o_dic; - o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"]; - o_array = [o_array arrayByAddingObject: o_dic]; - } - [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO]; - return YES; - } - } + b_activeDragAndDrop = NO; [self setNeedsDisplay:YES]; - return YES; } -- (void)concludeDragOperation:(id )sender +- (void)draggingExited:(id < NSDraggingInfo >)sender { + b_activeDragAndDrop = NO; [self setNeedsDisplay:YES]; } -@end - -/***************************************************************************** - * VLBrushedMetalImageView - *****************************************************************************/ - -@implementation VLBrushedMetalImageView - -- (BOOL)mouseDownCanMoveWindow +- (BOOL)prepareForDragOperation:(id )sender { return YES; } -- (void)dealloc +- (BOOL)performDragOperation:(id )sender { - [self unregisterDraggedTypes]; - [super dealloc]; -} + BOOL b_returned; -- (void)awakeFromNib -{ - [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType, - NSFilenamesPboardType, nil]]; -} + if (_dropHandler && [_dropHandler respondsToSelector:@selector(performDragOperation:)]) + b_returned = [_dropHandler performDragOperation: sender]; + else // default + b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender]; -- (NSDragOperation)draggingEntered:(id )sender -{ - if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) - == NSDragOperationGeneric) - { - return NSDragOperationGeneric; - } - else - { - return NSDragOperationNone; - } + [self setNeedsDisplay:YES]; + return b_returned; } -- (BOOL)prepareForDragOperation:(id )sender +- (void)concludeDragOperation:(id )sender { - return YES; + [self setNeedsDisplay:YES]; } -- (BOOL)performDragOperation:(id )sender +- (void)drawRect:(NSRect)dirtyRect { - NSPasteboard *o_paste = [sender draggingPasteboard]; - NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil]; - NSString *o_desired_type = [o_paste availableTypeFromArray:o_types]; - NSData *o_carried_data = [o_paste dataForType:o_desired_type]; - BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" ); + if ([self drawBorder] && b_activeDragAndDrop) { + NSRect frameRect = [self bounds]; - if( o_carried_data ) - { - if ([o_desired_type isEqualToString:NSFilenamesPboardType]) - { - int i; - NSArray *o_array = [NSArray array]; - NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] - sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; - - for( i = 0; i < (int)[o_values count]; i++) - { - NSDictionary *o_dic; - o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"]; - o_array = [o_array arrayByAddingObject: o_dic]; - } - if( b_autoplay ) - [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO]; - else - [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES]; - return YES; - } + [[NSColor selectedControlColor] set]; + NSFrameRectWithWidthUsingOperation(frameRect, 2., NSCompositeHighlight); } - [self setNeedsDisplay:YES]; - return YES; -} -- (void)concludeDragOperation:(id )sender -{ - [self setNeedsDisplay:YES]; + [super drawRect:dirtyRect]; } @end @@ -618,7 +426,7 @@ 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); @@ -645,7 +453,7 @@ void _drawFrameInRect(NSRect frameRect) NSRectClip(NSZeroRect); [super drawRect:rect]; [[NSGraphicsContext currentContext] restoreGraphicsState]; - + // Full size rect = [self bounds]; int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1; @@ -653,13 +461,13 @@ void _drawFrameInRect(NSRect frameRect) 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++; @@ -672,6 +480,201 @@ void _drawFrameInRect(NSRect frameRect) @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 (!OSX_SNOW_LEOPARD && [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 + *****************************************************************************/ + +@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; +} + +- (void)dealloc +{ + [o_knob_img release]; + [super dealloc]; +} + +- (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 + +@synthesize usesBrightArtwork = _usesBrightArtwork; + +- (void)scrollWheel:(NSEvent *)o_event +{ + BOOL b_up = NO; + CGFloat f_deltaY = [o_event deltaY]; + CGFloat f_deltaX = [o_event deltaX]; + + if (!OSX_SNOW_LEOPARD && [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 @@ -681,99 +684,319 @@ void _drawFrameInRect(NSRect frameRect) - (void)awakeFromNib { - if ([[self cell] class] != [ITSliderCell class]) { - // replace cell - NSSliderCell *oldCell = [self cell]; - NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease]; - [newCell setTag:[oldCell tag]]; - [newCell setTarget:[oldCell target]]; - [newCell setAction:[oldCell action]]; - [newCell setControlSize:[oldCell controlSize]]; - [newCell setType:[oldCell type]]; - [newCell setState:[oldCell state]]; - [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]]; - [newCell setAltIncrementValue:[oldCell altIncrementValue]]; - [newCell setControlTint:[oldCell controlTint]]; - [newCell setKnobThickness:[oldCell knobThickness]]; - [newCell setMaxValue:[oldCell maxValue]]; - [newCell setMinValue:[oldCell minValue]]; - [newCell setDoubleValue:[oldCell doubleValue]]; - [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]]; - [newCell setEditable:[oldCell isEditable]]; - [newCell setEnabled:[oldCell isEnabled]]; - [newCell setFormatter:[oldCell formatter]]; - [newCell setHighlighted:[oldCell isHighlighted]]; - [newCell setTickMarkPosition:[oldCell tickMarkPosition]]; - [self setCell:newCell]; - } + 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 /***************************************************************************** - * ITSliderCell + * VLCTimeField implementation + ***************************************************************************** + * we need this to catch our click-event in the controller window *****************************************************************************/ -@implementation ITSliderCell -- (id)init +@implementation VLCTimeField ++ (void)initialize { - self = [super init]; - _knobOff = [NSImage imageNamed:@"volumeslider_normal"]; - [self controlTintChanged]; - [[NSNotificationCenter defaultCenter] addObserver: self - selector: @selector( controlTintChanged ) - name: NSControlTintDidChangeNotification - object: nil]; - b_mouse_down = FALSE; - return self; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: + @"NO", @"DisplayTimeAsTimeRemaining", + @"YES", @"DisplayFullscreenTimeAsTimeRemaining", + nil]; + + [defaults registerDefaults:appDefaults]; } -- (void)controlTintChanged +- (void)setRemainingIdentifier:(NSString *)o_string { - if( [NSColor currentControlTint] == NSGraphiteControlTint ) - _knobOn = [NSImage imageNamed:@"volumeslider_graphite"]; - else - _knobOn = [NSImage imageNamed:@"volumeslider_blue"]; + 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)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver: self]; - [_knobOff release]; - [_knobOn release]; + [o_string_shadow release]; [super dealloc]; } -- (void)drawKnob:(NSRect)knob_rect +- (void)setStringValue:(NSString *)string { - NSImage *knob; + 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]; + [o_attributed_string release]; +} + +- (void)mouseDown: (NSEvent *)ourEvent +{ + if ( [ourEvent clickCount] > 1 ) + [[[VLCMain sharedInstance] controls] 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; + } + } +} - if( b_mouse_down ) - knob = _knobOn; +- (BOOL)timeRemaining +{ + if (o_remaining_identifier) + return [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier]; else - knob = _knobOff; + 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 + *****************************************************************************/ +@implementation VLCThreePartImageView + +- (void)dealloc +{ + [o_left_img release]; + [o_middle_img release]; + [o_right_img release]; + + [super dealloc]; +} + +- (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right +{ + if (o_left_img) + [o_left_img release]; + if (o_middle_img) + [o_middle_img release]; + if (o_right_img) + [o_right_img release]; + + o_left_img = [left retain]; + o_middle_img = [middle retain]; + o_right_img = [right retain]; +} + +- (void)drawRect:(NSRect)rect +{ + NSRect bnds = [self bounds]; + NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO ); +} + +@end + + +@implementation PositionFormatter + +- (id)init +{ + self = [super init]; + NSMutableCharacterSet *nonNumbers = [[[NSCharacterSet decimalDigitCharacterSet] invertedSet] mutableCopy]; + [nonNumbers removeCharactersInString:@"-:"]; + o_forbidden_characters = [nonNumbers copy]; + [nonNumbers release]; + + return self; +} + +- (void)dealloc +{ + [o_forbidden_characters release]; + [super dealloc]; +} + +- (NSString*)stringForObjectValue:(id)obj +{ + if([obj isKindOfClass:[NSString class]]) + return obj; + if([obj isKindOfClass:[NSNumber class]]) + return [obj stringValue]; + + return nil; +} - [[self controlView] lockFocus]; - [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1, - knob_rect.origin.y + knob_rect.size.height -2 ) - operation:NSCompositeSourceOver]; - [[self controlView] unlockFocus]; +- (BOOL)getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)error +{ + *obj = [[string copy] autorelease]; + return YES; } -- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView: - (NSView *)controlView mouseIsUp:(BOOL)flag +- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error { - b_mouse_down = NO; - [self drawKnob]; - [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag]; + if ([partialString rangeOfCharacterFromSet:o_forbidden_characters options:NSLiteralSearch].location != NSNotFound) { + return NO; + } else { + return YES; + } } -- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView +@end + +@implementation NSView (EnableSubviews) + +- (void)enableSubviews:(BOOL)b_enable { - b_mouse_down = YES; - [self drawKnob]; - return [super startTrackingAt:startPoint inView:controlView]; + 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]; + [theFormatter release]; + + return returnString; +} + +@end