1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2012 VLC authors and VideoLAN
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Felix Paul Kühne <fkuehne at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 #import "intf.h" /* VLCApplication */
27 #import "MainWindow.h"
28 #import "ControlsBar.h"
30 #import "CoreInteraction.h"
31 #import <CoreAudio/CoreAudio.h>
35 /*****************************************************************************
36 * NSSound (VLCAdditions)
38 * added code to change the system volume, needed for the apple remote code
39 * this is simplified code, which won't let you set the exact volume
40 * (that's what the audio output is for after all), but just the system volume
41 * in steps of 1/16 (matching the default AR or volume key implementation).
42 *****************************************************************************/
44 @implementation NSSound (VLCAdditions)
46 + (float)systemVolumeForChannel:(int)channel
48 AudioDeviceID i_device;
53 i_size = sizeof( i_device );
54 AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
55 err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
57 msg_Warn( VLCIntf, "couldn't get main audio output device" );
61 AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, channel };
62 i_size = sizeof( f_volume );
63 err = AudioObjectGetPropertyData(i_device, &propertyAddress, 0, NULL, &i_size, &f_volume);
65 msg_Warn( VLCIntf, "couldn't get volume value" );
72 + (bool)setSystemVolume:(float)f_volume forChannel:(int)i_channel
74 /* the following code will fail on S/PDIF devices. there is an easy work-around, but we'd like to match the OS behavior */
76 AudioDeviceID i_device;
81 i_size = sizeof( i_device );
82 AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
83 err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
85 msg_Warn( VLCIntf, "couldn't get main audio output device" );
89 AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, i_channel };
90 i_size = sizeof( f_volume );
91 err = AudioObjectIsPropertySettable( i_device, &propertyAddress, &b_writeable );
92 if (err != noErr || !b_writeable ) {
93 msg_Warn( VLCIntf, "we can't set the main audio devices' volume" );
96 err = AudioObjectSetPropertyData(i_device, &propertyAddress, 0, NULL, i_size, &f_volume);
101 + (void)increaseSystemVolume
103 float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
104 f_volume += .0625; // 1/16 to match the OS
105 bool b_returned = YES;
107 /* 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 */
108 for (NSUInteger x = 1; b_returned ; x++)
109 b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
112 + (void)decreaseSystemVolume
114 float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
115 f_volume -= .0625; // 1/16 to match the OS
116 bool b_returned = YES;
118 /* 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 */
119 for (NSUInteger x = 1; b_returned ; x++)
120 b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
125 /*****************************************************************************
126 * NSAnimation (VLCAdditions)
128 * Missing extension to NSAnimation
129 *****************************************************************************/
131 @implementation NSAnimation (VLCAdditions)
132 /* fake class attributes */
133 static NSMapTable *VLCAdditions_userInfo = NULL;
137 /* init our fake object attribute */
138 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
143 NSMapRemove(VLCAdditions_userInfo, self);
147 - (void)setUserInfo: (void *)userInfo
149 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
154 return NSMapGet(VLCAdditions_userInfo, self);
158 /*****************************************************************************
159 * NSScreen (VLCAdditions)
161 * Missing extension to NSScreen
162 *****************************************************************************/
164 @implementation NSScreen (VLCAdditions)
166 static NSMutableArray *blackoutWindows = NULL;
170 /* init our fake object attribute */
171 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
174 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
176 NSUInteger count = [[NSScreen screens] count];
178 for ( NSUInteger i = 0; i < count; i++ ) {
179 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
180 if ([screen displayID] == displayID)
188 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
191 - (BOOL)isScreen: (NSScreen*)screen
193 return ([self displayID] == [screen displayID]);
196 - (CGDirectDisplayID)displayID
198 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
201 - (void)blackoutOtherScreens
203 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
204 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
205 [blackoutWindows removeAllObjects];
207 NSUInteger screenCount = [[NSScreen screens] count];
208 for (NSUInteger i = 0; i < screenCount; i++) {
209 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
210 VLCWindow *blackoutWindow;
213 if ([self isScreen: screen])
216 screen_rect = [screen frame];
217 screen_rect.origin.x = screen_rect.origin.y = 0;
219 /* blackoutWindow alloc strategy
220 - The NSMutableArray blackoutWindows has the blackoutWindow references
221 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
223 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
224 backing: NSBackingStoreBuffered defer: NO screen: screen];
225 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
226 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
228 [blackoutWindow displayIfNeeded];
229 [blackoutWindow orderFront: self animate: YES];
231 [blackoutWindows addObject: blackoutWindow];
232 [blackoutWindow release];
234 if ( [screen mainScreen] )
235 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
239 + (void)unblackoutScreens
241 NSUInteger blackoutWindowCount = [blackoutWindows count];
243 for (NSUInteger i = 0; i < blackoutWindowCount; i++) {
244 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
245 [blackoutWindow closeAndAnimate: YES];
248 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
253 /*****************************************************************************
254 * VLBrushedMetalImageView
255 *****************************************************************************/
257 @implementation VLBrushedMetalImageView
259 - (BOOL)mouseDownCanMoveWindow
266 [self unregisterDraggedTypes];
272 [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
273 [self setImageScaling: NSScaleToFit];
274 [self setImageFrameStyle: NSImageFrameNone];
275 [self setImageAlignment: NSImageAlignCenter];
278 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
280 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
281 return NSDragOperationGeneric;
283 return NSDragOperationNone;
286 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
291 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
294 b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
296 [self setNeedsDisplay:YES];
300 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
302 [self setNeedsDisplay:YES];
308 /*****************************************************************************
310 *****************************************************************************/
311 @implementation MPSlider
313 void _drawKnobInRect(NSRect knobRect)
315 // Center knob in given rect
316 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
317 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
320 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
321 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
322 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
323 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
324 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
325 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
326 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
329 void _drawFrameInRect(NSRect frameRect)
332 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
333 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
334 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
335 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
338 - (void)drawRect:(NSRect)rect
340 // Draw default to make sure the slider behaves correctly
341 [[NSGraphicsContext currentContext] saveGraphicsState];
342 NSRectClip(NSZeroRect);
343 [super drawRect:rect];
344 [[NSGraphicsContext currentContext] restoreGraphicsState];
347 rect = [self bounds];
348 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
349 rect.origin.x += diff-1;
350 rect.origin.y += diff;
351 rect.size.width -= 2*diff-2;
352 rect.size.height -= 2*diff;
355 NSRect knobRect = [[self cell] knobRectFlipped:NO];
356 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
357 _drawFrameInRect(rect);
358 _drawKnobInRect(knobRect);
361 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
366 _drawFrameInRect(rect);
367 _drawKnobInRect(knobRect);
372 /*****************************************************************************
374 *****************************************************************************/
376 @implementation VLCProgressView : NSView
378 - (void)scrollWheel:(NSEvent *)o_event
380 intf_thread_t * p_intf = VLCIntf;
381 CGFloat f_deltaY = [o_event deltaY];
382 CGFloat f_deltaX = [o_event deltaX];
384 if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
385 f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
387 f_deltaY = -f_deltaY;
389 // positive for left / down, negative otherwise
390 CGFloat f_delta = f_deltaX + f_deltaY;
394 if (f_delta > 0.0f) {
395 i_vlckey = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
399 i_vlckey = ACTIONID_JUMP_FORWARD_EXTRASHORT;
403 for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
404 var_SetInteger( p_intf->p_libvlc, "key-action", i_vlckey );
407 - (BOOL)acceptsFirstResponder
414 /*****************************************************************************
416 *****************************************************************************/
418 @implementation TimeLineSlider
422 if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
423 o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
426 o_knob_img = [NSImage imageNamed:@"progression-knob"];
429 img_rect.size = [o_knob_img size];
430 img_rect.origin.x = img_rect.origin.y = 0;
435 [o_knob_img release];
439 - (CGFloat)knobPosition
441 NSRect knobRect = [[self cell] knobRectFlipped:NO];
442 knobRect.origin.x += knobRect.size.width / 2;
443 return knobRect.origin.x;
446 - (void)drawKnobInRect:(NSRect)knobRect
448 knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
449 knobRect.size.width = img_rect.size.width;
450 knobRect.size.height = img_rect.size.height;
451 [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
454 - (void)drawRect:(NSRect)rect
456 [[[[VLCMain sharedInstance] mainWindow] controlsBar] drawFancyGradientEffectForTimeSlider];
457 msleep( 10000 ); //wait for the gradient to draw completely
459 /* Draw default to make sure the slider behaves correctly */
460 [[NSGraphicsContext currentContext] saveGraphicsState];
461 NSRectClip(NSZeroRect);
462 [super drawRect:rect];
463 [[NSGraphicsContext currentContext] restoreGraphicsState];
465 NSRect knobRect = [[self cell] knobRectFlipped:NO];
467 knobRect.origin.y+=2;
469 knobRect.origin.y+=1;
470 [self drawKnobInRect: knobRect];
475 /*****************************************************************************
476 * VLCVolumeSliderCommon
477 *****************************************************************************/
479 @implementation VLCVolumeSliderCommon : NSSlider
481 - (void)scrollWheel:(NSEvent *)o_event
483 intf_thread_t * p_intf = VLCIntf;
484 CGFloat f_deltaY = [o_event deltaY];
485 CGFloat f_deltaX = [o_event deltaX];
487 if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
488 f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
490 f_deltaY = -f_deltaY;
492 // positive for left / down, negative otherwise
493 CGFloat f_delta = f_deltaX + f_deltaY;
497 if (f_delta > 0.0f) {
498 i_vlckey = ACTIONID_VOL_DOWN;
502 i_vlckey = ACTIONID_VOL_UP;
506 for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
507 var_SetInteger(p_intf->p_libvlc, "key-action", i_vlckey);
512 /*****************************************************************************
514 *****************************************************************************/
516 @implementation ITSlider
520 BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
522 img = [NSImage imageNamed:@"volume-slider-knob_dark"];
524 img = [NSImage imageNamed:@"volume-slider-knob"];
526 image_rect.size = [img size];
527 image_rect.origin.x = 0;
530 image_rect.origin.y = -1;
532 image_rect.origin.y = 0;
535 - (void)drawKnobInRect:(NSRect)knobRect
537 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
538 knobRect.size.width = image_rect.size.width;
539 knobRect.size.height = image_rect.size.height;
540 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
543 - (void)drawRect:(NSRect)rect
545 /* Draw default to make sure the slider behaves correctly */
546 [[NSGraphicsContext currentContext] saveGraphicsState];
547 NSRectClip(NSZeroRect);
548 [super drawRect:rect];
549 [[NSGraphicsContext currentContext] restoreGraphicsState];
551 NSRect knobRect = [[self cell] knobRectFlipped:NO];
552 knobRect.origin.y+=2;
553 [self drawKnobInRect: knobRect];
558 /*****************************************************************************
559 * VLCTimeField implementation
560 *****************************************************************************
561 * we need this to catch our click-event in the controller window
562 *****************************************************************************/
564 @implementation VLCTimeField
566 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
567 NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
569 [defaults registerDefaults:appDefaults];
574 NSColor *o_string_color;
575 if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
576 o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
578 o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
580 textAlignment = NSCenterTextAlignment;
581 o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
584 - (void)setAlignment:(NSTextAlignment)alignment
586 textAlignment = alignment;
587 [self setStringValue:[self stringValue]];
592 [o_string_shadow release];
593 [o_string_attributes_dict release];
597 - (void)setStringValue:(NSString *)string
599 if (!o_string_shadow) {
600 o_string_shadow = [[NSShadow alloc] init];
601 [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
602 [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
603 [o_string_shadow setShadowBlurRadius:0.0];
606 NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
607 NSUInteger i_stringLength = [string length];
609 [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
610 [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
611 [self setAttributedStringValue: o_attributed_string];
612 [o_attributed_string release];
615 - (void)mouseDown: (NSEvent *)ourEvent
617 if ( [ourEvent clickCount] > 1 )
618 [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
621 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
622 [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
624 [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
628 - (BOOL)timeRemaining
630 return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
634 /*****************************************************************************
635 * VLCMainWindowSplitView implementation
636 * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
637 *****************************************************************************/
638 @implementation VLCMainWindowSplitView : NSSplitView
639 /* 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.
641 - (NSColor *)dividerColor
643 return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
646 /* 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.
648 - (CGFloat)dividerThickness
654 /*****************************************************************************
655 * VLCThreePartImageView interface
656 *****************************************************************************/
657 @implementation VLCThreePartImageView
661 [o_left_img release];
662 [o_middle_img release];
663 [o_right_img release];
668 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
671 [o_left_img release];
673 [o_middle_img release];
675 [o_right_img release];
677 o_left_img = [left retain];
678 o_middle_img = [middle retain];
679 o_right_img = [right retain];
682 - (void)drawRect:(NSRect)rect
684 NSRect bnds = [self bounds];
685 NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
690 @implementation VLCThreePartDropView
692 - (BOOL)mouseDownCanMoveWindow
699 [self unregisterDraggedTypes];
705 [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
708 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
710 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
711 return NSDragOperationGeneric;
713 return NSDragOperationNone;
716 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
721 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
724 b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
726 [self setNeedsDisplay:YES];
730 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
732 [self setNeedsDisplay:YES];