1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2011 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 *****************************************************************************/
25 #import <Cocoa/Cocoa.h>
26 #import <Carbon/Carbon.h>
28 #import "CompatibilityFixes.h"
29 #import "intf.h" /* VLCApplication */
30 #import "MainWindow.h"
36 /*****************************************************************************
37 * NSAnimation (VLCAdditions)
39 * Missing extension to NSAnimation
40 *****************************************************************************/
42 @implementation NSAnimation (VLCAdditions)
43 /* fake class attributes */
44 static NSMapTable *VLCAdditions_userInfo = NULL;
48 /* init our fake object attribute */
49 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
54 NSMapRemove(VLCAdditions_userInfo, self);
58 - (void)setUserInfo: (void *)userInfo
60 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
65 return NSMapGet(VLCAdditions_userInfo, self);
69 /*****************************************************************************
70 * NSScreen (VLCAdditions)
72 * Missing extension to NSScreen
73 *****************************************************************************/
75 @implementation NSScreen (VLCAdditions)
77 static NSMutableArray *blackoutWindows = NULL;
81 /* init our fake object attribute */
82 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
85 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
87 NSUInteger count = [[NSScreen screens] count];
89 for( NSUInteger i = 0; i < count; i++ )
91 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
92 if([screen displayID] == displayID)
100 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
103 - (BOOL)isScreen: (NSScreen*)screen
105 return ([self displayID] == [screen displayID]);
108 - (CGDirectDisplayID)displayID
110 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
113 - (void)blackoutOtherScreens
115 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
116 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
117 [blackoutWindows removeAllObjects];
119 NSUInteger screenCount = [[NSScreen screens] count];
120 for(NSUInteger i = 0; i < screenCount; i++)
122 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
123 VLCWindow *blackoutWindow;
126 if([self isScreen: screen])
129 screen_rect = [screen frame];
130 screen_rect.origin.x = screen_rect.origin.y = 0;
132 /* blackoutWindow alloc strategy
133 - The NSMutableArray blackoutWindows has the blackoutWindow references
134 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
136 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
137 backing: NSBackingStoreBuffered defer: NO screen: screen];
138 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
139 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
141 [blackoutWindow displayIfNeeded];
142 [blackoutWindow orderFront: self animate: YES];
144 [blackoutWindows addObject: blackoutWindow];
145 [blackoutWindow release];
147 if( [screen isMainScreen ] )
149 if ([screen isMainScreen])
152 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
154 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
160 + (void)unblackoutScreens
162 NSUInteger blackoutWindowCount = [blackoutWindows count];
164 for(NSUInteger i = 0; i < blackoutWindowCount; i++)
166 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
167 [blackoutWindow closeAndAnimate: YES];
171 SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
173 [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
178 /*****************************************************************************
181 * Missing extension to NSWindow
182 *****************************************************************************/
184 @implementation VLCWindow
185 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
186 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
188 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
191 b_isset_canBecomeKeyWindow = NO;
192 /* we don't want this window to be restored on relaunch */
194 [self setRestorable:NO];
198 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
200 b_isset_canBecomeKeyWindow = YES;
201 b_canBecomeKeyWindow = canBecomeKey;
204 - (BOOL)canBecomeKeyWindow
206 if(b_isset_canBecomeKeyWindow)
207 return b_canBecomeKeyWindow;
209 return [super canBecomeKeyWindow];
212 - (void)closeAndAnimate: (BOOL)animate
222 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
223 [invoc setTarget: self];
225 if (![self isVisible] || [self alphaValue] == 0.0)
231 [self orderOut: self animate: YES callback: invoc];
234 - (void)orderOut: (id)sender animate: (BOOL)animate
236 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
237 [invoc setTarget: self];
238 [invoc setArgument: sender atIndex: 0];
239 [self orderOut: sender animate: animate callback: invoc];
242 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
244 NSViewAnimation *anim;
245 NSViewAnimation *current_anim;
246 NSMutableDictionary *dict;
250 [self orderOut: sender];
254 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
256 [dict setObject:self forKey:NSViewAnimationTargetKey];
258 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
259 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
262 [anim setAnimationBlockingMode:NSAnimationNonblocking];
263 [anim setDuration:0.9];
264 [anim setFrameRate:30];
265 [anim setUserInfo: callback];
267 @synchronized(self) {
268 current_anim = self->animation;
270 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
278 [current_anim stopAnimation];
279 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
280 [current_anim release];
283 [anim setCurrentProgress:1.0 - [self alphaValue]];
284 self->animation = anim;
285 [self setDelegate: self];
286 [anim startAnimation];
291 - (void)orderFront: (id)sender animate: (BOOL)animate
293 NSViewAnimation *anim;
294 NSViewAnimation *current_anim;
295 NSMutableDictionary *dict;
299 [super orderFront: sender];
300 [self setAlphaValue: 1.0];
304 if (![self isVisible])
306 [self setAlphaValue: 0.0];
307 [super orderFront: sender];
309 else if ([self alphaValue] == 1.0)
311 [super orderFront: self];
315 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
317 [dict setObject:self forKey:NSViewAnimationTargetKey];
319 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
320 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
323 [anim setAnimationBlockingMode:NSAnimationNonblocking];
324 [anim setDuration:0.5];
325 [anim setFrameRate:30];
327 @synchronized(self) {
328 current_anim = self->animation;
330 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
338 [current_anim stopAnimation];
339 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
340 [current_anim release];
343 [anim setCurrentProgress:[self alphaValue]];
344 self->animation = anim;
345 [self setDelegate: self];
346 [self orderFront: sender];
347 [anim startAnimation];
352 - (void)animationDidEnd:(NSAnimation*)anim
354 if ([self alphaValue] <= 0.0)
356 NSInvocation * invoc;
357 [super orderOut: nil];
358 [self setAlphaValue: 1.0];
359 if ((invoc = [anim userInfo]))
365 /*****************************************************************************
367 *****************************************************************************/
369 @implementation VLCControllerView
373 [self unregisterDraggedTypes];
379 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
380 NSFilenamesPboardType, nil]];
383 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
385 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
386 == NSDragOperationGeneric)
388 return NSDragOperationGeneric;
392 return NSDragOperationNone;
396 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
401 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
403 NSPasteboard *o_paste = [sender draggingPasteboard];
404 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
405 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
406 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
410 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
412 NSArray *o_array = [NSArray array];
413 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
414 NSUInteger count = [o_values count];
416 for( NSUInteger i = 0; i < count; i++)
419 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
423 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
426 o_array = [o_array arrayByAddingObject: o_dic];
428 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
432 [self setNeedsDisplay:YES];
436 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
438 [self setNeedsDisplay:YES];
443 /*****************************************************************************
444 * VLBrushedMetalImageView
445 *****************************************************************************/
447 @implementation VLBrushedMetalImageView
449 - (BOOL)mouseDownCanMoveWindow
456 [self unregisterDraggedTypes];
462 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
463 NSFilenamesPboardType, nil]];
464 [self setImageScaling: NSScaleToFit];
465 [self setImageFrameStyle: NSImageFrameNone];
466 [self setImageAlignment: NSImageAlignCenter];
469 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
471 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
472 == NSDragOperationGeneric)
474 return NSDragOperationGeneric;
478 return NSDragOperationNone;
482 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
487 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
489 NSPasteboard *o_paste = [sender draggingPasteboard];
490 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
491 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
492 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
493 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
497 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
499 NSArray *o_array = [NSArray array];
500 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
501 NSUInteger count = [o_values count];
503 for( NSUInteger i = 0; i < count; i++)
506 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
510 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
513 o_array = [o_array arrayByAddingObject: o_dic];
516 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
518 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
522 [self setNeedsDisplay:YES];
526 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
528 [self setNeedsDisplay:YES];
534 /*****************************************************************************
536 *****************************************************************************/
537 @implementation MPSlider
539 void _drawKnobInRect(NSRect knobRect)
541 // Center knob in given rect
542 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
543 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
546 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
547 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
548 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
549 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
550 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
551 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
552 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
555 void _drawFrameInRect(NSRect frameRect)
558 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
559 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
560 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
561 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
564 - (void)drawRect:(NSRect)rect
566 // Draw default to make sure the slider behaves correctly
567 [[NSGraphicsContext currentContext] saveGraphicsState];
568 NSRectClip(NSZeroRect);
569 [super drawRect:rect];
570 [[NSGraphicsContext currentContext] restoreGraphicsState];
573 rect = [self bounds];
574 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
575 rect.origin.x += diff-1;
576 rect.origin.y += diff;
577 rect.size.width -= 2*diff-2;
578 rect.size.height -= 2*diff;
581 NSRect knobRect = [[self cell] knobRectFlipped:NO];
582 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
583 _drawFrameInRect(rect);
584 _drawKnobInRect(knobRect);
587 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
592 _drawFrameInRect(rect);
593 _drawKnobInRect(knobRect);
598 /*****************************************************************************
600 *****************************************************************************/
602 @implementation TimeLineSlider
604 - (void)drawKnobInRect:(NSRect)knobRect
607 NSImage *img = [NSImage imageNamed:@"progression-knob"];
608 image_rect.size = [img size];
609 image_rect.origin.x = 0;
610 image_rect.origin.y = 0;
611 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
612 knobRect.size.width = image_rect.size.width;
613 knobRect.size.height = image_rect.size.height;
614 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
617 - (void)drawRect:(NSRect)rect
619 /* Draw default to make sure the slider behaves correctly */
620 [[NSGraphicsContext currentContext] saveGraphicsState];
621 NSRectClip(NSZeroRect);
622 [super drawRect:rect];
623 [[NSGraphicsContext currentContext] restoreGraphicsState];
625 NSRect knobRect = [[self cell] knobRectFlipped:NO];
626 knobRect.origin.y+=1;
627 [self drawKnobInRect: knobRect];
632 /*****************************************************************************
634 *****************************************************************************/
636 @implementation ITSlider
638 - (void)drawKnobInRect:(NSRect)knobRect
641 NSImage *img = [NSImage imageNamed:@"volume-slider-knob"];
642 image_rect.size = [img size];
643 image_rect.origin.x = 0;
644 image_rect.origin.y = 0;
645 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
646 knobRect.size.width = image_rect.size.width;
647 knobRect.size.height = image_rect.size.height;
648 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
651 - (void)drawRect:(NSRect)rect
653 /* Draw default to make sure the slider behaves correctly */
654 [[NSGraphicsContext currentContext] saveGraphicsState];
655 NSRectClip(NSZeroRect);
656 [super drawRect:rect];
657 [[NSGraphicsContext currentContext] restoreGraphicsState];
659 NSRect knobRect = [[self cell] knobRectFlipped:NO];
660 knobRect.origin.y+=2;
661 [self drawKnobInRect: knobRect];
666 /*****************************************************************************
667 * VLCTimeField implementation
668 *****************************************************************************
669 * we need this to catch our click-event in the controller window
670 *****************************************************************************/
672 @implementation VLCTimeField
674 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
675 NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
677 [defaults registerDefaults:appDefaults];
680 - (void)mouseDown: (NSEvent *)ourEvent
682 if( [ourEvent clickCount] > 1 )
683 [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
686 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
687 [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
689 [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
693 - (BOOL)timeRemaining
695 return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
699 /*****************************************************************************
700 * VLCMainWindowSplitView implementation
701 * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
702 *****************************************************************************/
703 @implementation VLCMainWindowSplitView : NSSplitView
704 /* 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.
706 - (NSColor *)dividerColor
708 return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
711 /* 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.
713 - (CGFloat)dividerThickness
718 - (void)adjustSubviews
720 NSArray *o_subviews = [self subviews];
721 NSRect viewDimensions = [self frame];
722 NSRect leftViewDimensions = [[o_subviews objectAtIndex:0] frame];
723 NSRect rightViewDimensions = [[o_subviews objectAtIndex:1] frame];
724 CGFloat f_dividerThickness = [self dividerThickness];
726 leftViewDimensions.size.height = viewDimensions.size.height;
727 [[o_subviews objectAtIndex:0] setFrame: leftViewDimensions];
729 rightViewDimensions.origin.x = leftViewDimensions.size.width + f_dividerThickness;
730 rightViewDimensions.size.width = viewDimensions.size.width - leftViewDimensions.size.width - f_dividerThickness;
731 rightViewDimensions.size.height = viewDimensions.size.height;
732 [[o_subviews objectAtIndex:1] setFrame: rightViewDimensions];