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]);
193 NSRect screen_frame = [self frame];
194 NSRect screen_visible_frame = [self visibleFrame];
195 CGFloat f_menu_bar_thickness = [self hasMenuBar] ? [[NSStatusBar systemStatusBar] thickness] : 0.0;
197 BOOL b_found_dock = NO;
198 if (screen_visible_frame.size.width < screen_frame.size.width)
200 else if (screen_visible_frame.size.height + f_menu_bar_thickness < screen_frame.size.height)
206 - (BOOL)isScreen: (NSScreen*)screen
208 return ([self displayID] == [screen displayID]);
211 - (CGDirectDisplayID)displayID
213 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
216 - (void)blackoutOtherScreens
218 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
219 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
220 [blackoutWindows removeAllObjects];
222 NSUInteger screenCount = [[NSScreen screens] count];
223 for (NSUInteger i = 0; i < screenCount; i++) {
224 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
225 VLCWindow *blackoutWindow;
228 if ([self isScreen: screen])
231 screen_rect = [screen frame];
232 screen_rect.origin.x = screen_rect.origin.y = 0;
234 /* blackoutWindow alloc strategy
235 - The NSMutableArray blackoutWindows has the blackoutWindow references
236 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
238 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
239 backing: NSBackingStoreBuffered defer: NO screen: screen];
240 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
241 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
243 [blackoutWindow displayIfNeeded];
244 [blackoutWindow orderFront: self animate: YES];
246 [blackoutWindows addObject: blackoutWindow];
247 [blackoutWindow release];
249 NSApplicationPresentationOptions presentationOpts = [NSApp presentationOptions];
250 if ([screen hasMenuBar])
251 presentationOpts |= NSApplicationPresentationAutoHideMenuBar;
252 if ([screen hasMenuBar] || [screen hasDock])
253 presentationOpts |= NSApplicationPresentationAutoHideDock;
254 [NSApp setPresentationOptions:presentationOpts];
258 + (void)unblackoutScreens
260 NSUInteger blackoutWindowCount = [blackoutWindows count];
262 for (NSUInteger i = 0; i < blackoutWindowCount; i++) {
263 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
264 [blackoutWindow closeAndAnimate: YES];
267 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
272 /*****************************************************************************
273 * VLBrushedMetalImageView
274 *****************************************************************************/
276 @implementation VLBrushedMetalImageView
278 - (BOOL)mouseDownCanMoveWindow
285 [self unregisterDraggedTypes];
291 [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
292 [self setImageScaling: NSScaleToFit];
293 [self setImageFrameStyle: NSImageFrameNone];
294 [self setImageAlignment: NSImageAlignCenter];
297 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
299 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
300 return NSDragOperationGeneric;
302 return NSDragOperationNone;
305 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
310 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
313 b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
315 [self setNeedsDisplay:YES];
319 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
321 [self setNeedsDisplay:YES];
327 /*****************************************************************************
329 *****************************************************************************/
330 @implementation MPSlider
332 void _drawKnobInRect(NSRect knobRect)
334 // Center knob in given rect
335 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
336 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
339 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
340 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
341 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
342 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
343 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
344 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
345 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
348 void _drawFrameInRect(NSRect frameRect)
351 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
352 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
353 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
354 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
357 - (void)drawRect:(NSRect)rect
359 // Draw default to make sure the slider behaves correctly
360 [[NSGraphicsContext currentContext] saveGraphicsState];
361 NSRectClip(NSZeroRect);
362 [super drawRect:rect];
363 [[NSGraphicsContext currentContext] restoreGraphicsState];
366 rect = [self bounds];
367 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
368 rect.origin.x += diff-1;
369 rect.origin.y += diff;
370 rect.size.width -= 2*diff-2;
371 rect.size.height -= 2*diff;
374 NSRect knobRect = [[self cell] knobRectFlipped:NO];
375 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
376 _drawFrameInRect(rect);
377 _drawKnobInRect(knobRect);
380 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
385 _drawFrameInRect(rect);
386 _drawKnobInRect(knobRect);
391 /*****************************************************************************
393 *****************************************************************************/
395 @implementation VLCProgressView : NSView
397 - (void)scrollWheel:(NSEvent *)o_event
399 intf_thread_t * p_intf = VLCIntf;
400 CGFloat f_deltaY = [o_event deltaY];
401 CGFloat f_deltaX = [o_event deltaX];
403 if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
404 f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
406 f_deltaY = -f_deltaY;
408 // positive for left / down, negative otherwise
409 CGFloat f_delta = f_deltaX + f_deltaY;
413 if (f_delta > 0.0f) {
414 i_vlckey = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
418 i_vlckey = ACTIONID_JUMP_FORWARD_EXTRASHORT;
422 for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
423 var_SetInteger( p_intf->p_libvlc, "key-action", i_vlckey );
426 - (BOOL)acceptsFirstResponder
433 /*****************************************************************************
435 *****************************************************************************/
437 @implementation TimeLineSlider
441 if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
442 o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
445 o_knob_img = [NSImage imageNamed:@"progression-knob"];
448 img_rect.size = [o_knob_img size];
449 img_rect.origin.x = img_rect.origin.y = 0;
454 [o_knob_img release];
458 - (CGFloat)knobPosition
460 NSRect knobRect = [[self cell] knobRectFlipped:NO];
461 knobRect.origin.x += knobRect.size.width / 2;
462 return knobRect.origin.x;
465 - (void)drawKnobInRect:(NSRect)knobRect
467 knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
468 knobRect.size.width = img_rect.size.width;
469 knobRect.size.height = img_rect.size.height;
470 [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
473 - (void)drawRect:(NSRect)rect
475 [[[[VLCMain sharedInstance] mainWindow] controlsBar] drawFancyGradientEffectForTimeSlider];
476 msleep( 10000 ); //wait for the gradient to draw completely
478 /* Draw default to make sure the slider behaves correctly */
479 [[NSGraphicsContext currentContext] saveGraphicsState];
480 NSRectClip(NSZeroRect);
481 [super drawRect:rect];
482 [[NSGraphicsContext currentContext] restoreGraphicsState];
484 NSRect knobRect = [[self cell] knobRectFlipped:NO];
486 knobRect.origin.y+=2;
488 knobRect.origin.y+=1;
489 [self drawKnobInRect: knobRect];
494 /*****************************************************************************
495 * VLCVolumeSliderCommon
496 *****************************************************************************/
498 @implementation VLCVolumeSliderCommon : NSSlider
500 - (void)scrollWheel:(NSEvent *)o_event
502 intf_thread_t * p_intf = VLCIntf;
503 CGFloat f_deltaY = [o_event deltaY];
504 CGFloat f_deltaX = [o_event deltaX];
506 if (!OSX_SNOW_LEOPARD && [o_event isDirectionInvertedFromDevice])
507 f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
509 f_deltaY = -f_deltaY;
511 // positive for left / down, negative otherwise
512 CGFloat f_delta = f_deltaX + f_deltaY;
516 if (f_delta > 0.0f) {
517 i_vlckey = ACTIONID_VOL_DOWN;
521 i_vlckey = ACTIONID_VOL_UP;
525 for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++)
526 var_SetInteger(p_intf->p_libvlc, "key-action", i_vlckey);
531 /*****************************************************************************
533 *****************************************************************************/
535 @implementation ITSlider
539 BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
541 img = [NSImage imageNamed:@"volume-slider-knob_dark"];
543 img = [NSImage imageNamed:@"volume-slider-knob"];
545 image_rect.size = [img size];
546 image_rect.origin.x = 0;
549 image_rect.origin.y = -1;
551 image_rect.origin.y = 0;
554 - (void)drawKnobInRect:(NSRect)knobRect
556 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
557 knobRect.size.width = image_rect.size.width;
558 knobRect.size.height = image_rect.size.height;
559 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
562 - (void)drawRect:(NSRect)rect
564 /* Draw default to make sure the slider behaves correctly */
565 [[NSGraphicsContext currentContext] saveGraphicsState];
566 NSRectClip(NSZeroRect);
567 [super drawRect:rect];
568 [[NSGraphicsContext currentContext] restoreGraphicsState];
570 NSRect knobRect = [[self cell] knobRectFlipped:NO];
571 knobRect.origin.y+=2;
572 [self drawKnobInRect: knobRect];
577 /*****************************************************************************
578 * VLCTimeField implementation
579 *****************************************************************************
580 * we need this to catch our click-event in the controller window
581 *****************************************************************************/
583 @implementation VLCTimeField
585 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
586 NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
588 [defaults registerDefaults:appDefaults];
593 NSColor *o_string_color;
594 if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
595 o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
597 o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
599 textAlignment = NSCenterTextAlignment;
600 o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
603 - (void)setAlignment:(NSTextAlignment)alignment
605 textAlignment = alignment;
606 [self setStringValue:[self stringValue]];
611 [o_string_shadow release];
612 [o_string_attributes_dict release];
616 - (void)setStringValue:(NSString *)string
618 if (!o_string_shadow) {
619 o_string_shadow = [[NSShadow alloc] init];
620 [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
621 [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
622 [o_string_shadow setShadowBlurRadius:0.0];
625 NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
626 NSUInteger i_stringLength = [string length];
628 [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
629 [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
630 [self setAttributedStringValue: o_attributed_string];
631 [o_attributed_string release];
634 - (void)mouseDown: (NSEvent *)ourEvent
636 if ( [ourEvent clickCount] > 1 )
637 [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
640 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
641 [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
643 [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
647 - (BOOL)timeRemaining
649 return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
653 /*****************************************************************************
654 * VLCMainWindowSplitView implementation
655 * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
656 *****************************************************************************/
657 @implementation VLCMainWindowSplitView : NSSplitView
658 /* 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.
660 - (NSColor *)dividerColor
662 return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
665 /* 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.
667 - (CGFloat)dividerThickness
673 /*****************************************************************************
674 * VLCThreePartImageView interface
675 *****************************************************************************/
676 @implementation VLCThreePartImageView
680 [o_left_img release];
681 [o_middle_img release];
682 [o_right_img release];
687 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
690 [o_left_img release];
692 [o_middle_img release];
694 [o_right_img release];
696 o_left_img = [left retain];
697 o_middle_img = [middle retain];
698 o_right_img = [right retain];
701 - (void)drawRect:(NSRect)rect
703 NSRect bnds = [self bounds];
704 NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
709 @implementation VLCThreePartDropView
711 - (BOOL)mouseDownCanMoveWindow
718 [self unregisterDraggedTypes];
724 [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
727 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
729 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
730 return NSDragOperationGeneric;
732 return NSDragOperationNone;
735 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
740 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
743 b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
745 [self setNeedsDisplay:YES];
749 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
751 [self setNeedsDisplay:YES];