1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2011 the VideoLAN team
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 "intf.h" /* VLCApplication */
34 /*****************************************************************************
35 * NSImage (VLCAdditions)
38 *****************************************************************************/
39 @implementation NSImage (VLCAdditions)
40 + (id)imageWithSystemName:(int)name
42 /* ugly Carbon stuff following...
43 * regrettably, you can't get the icons through clean Cocoa */
45 /* retrieve our error icon */
49 returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef);
50 icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease];
52 CGRect rect = CGRectMake(0,0,32,32);
53 PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
58 NULL /*inLabelColor*/,
59 kPlotIconRefNormalFlags,
62 returnValue = ReleaseIconRef(ourIconRef);
66 + (id)imageWithWarningIcon
68 static NSImage * imageWithWarningIcon = nil;
69 if( !imageWithWarningIcon )
71 imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain];
73 return imageWithWarningIcon;
76 + (id)imageWithErrorIcon
78 static NSImage * imageWithErrorIcon = nil;
79 if( !imageWithErrorIcon )
81 imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain];
83 return imageWithErrorIcon;
87 /*****************************************************************************
88 * NSAnimation (VLCAdditions)
90 * Missing extension to NSAnimation
91 *****************************************************************************/
93 @implementation NSAnimation (VLCAdditions)
94 /* fake class attributes */
95 static NSMapTable *VLCAdditions_userInfo = NULL;
99 /* init our fake object attribute */
100 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
105 NSMapRemove(VLCAdditions_userInfo, self);
109 - (void)setUserInfo: (void *)userInfo
111 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
116 return NSMapGet(VLCAdditions_userInfo, self);
120 /*****************************************************************************
121 * NSScreen (VLCAdditions)
123 * Missing extension to NSScreen
124 *****************************************************************************/
126 @implementation NSScreen (VLCAdditions)
128 static NSMutableArray *blackoutWindows = NULL;
132 /* init our fake object attribute */
133 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
136 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
140 for( i = 0; i < [[NSScreen screens] count]; i++ )
142 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
143 if([screen displayID] == displayID)
151 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
154 - (BOOL)isScreen: (NSScreen*)screen
156 return ([self displayID] == [screen displayID]);
159 - (CGDirectDisplayID)displayID
161 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
164 - (void)blackoutOtherScreens
168 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
169 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
170 [blackoutWindows removeAllObjects];
172 for(i = 0; i < [[NSScreen screens] count]; i++)
174 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
175 VLCWindow *blackoutWindow;
178 if([self isScreen: screen])
181 screen_rect = [screen frame];
182 screen_rect.origin.x = screen_rect.origin.y = 0;
184 /* blackoutWindow alloc strategy
185 - The NSMutableArray blackoutWindows has the blackoutWindow references
186 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
188 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
189 backing: NSBackingStoreBuffered defer: NO screen: screen];
190 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
191 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
193 [blackoutWindow displayIfNeeded];
194 [blackoutWindow orderFront: self animate: YES];
196 [blackoutWindows addObject: blackoutWindow];
197 [blackoutWindow release];
199 if( [screen isMainScreen ] )
200 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
204 + (void)unblackoutScreens
208 for(i = 0; i < [blackoutWindows count]; i++)
210 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
211 [blackoutWindow closeAndAnimate: YES];
214 SetSystemUIMode( kUIModeNormal, 0);
219 /*****************************************************************************
222 * Missing extension to NSWindow
223 *****************************************************************************/
225 @implementation VLCWindow
226 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
227 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
229 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
232 b_isset_canBecomeKeyWindow = NO;
233 /* we don't want this window to be restored on relaunch */
234 if ([self respondsToSelector:@selector(setRestorable:)])
235 [self setRestorable:NO];
239 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
241 b_isset_canBecomeKeyWindow = YES;
242 b_canBecomeKeyWindow = canBecomeKey;
245 - (BOOL)canBecomeKeyWindow
247 if(b_isset_canBecomeKeyWindow)
248 return b_canBecomeKeyWindow;
250 return [super canBecomeKeyWindow];
253 - (void)closeAndAnimate: (BOOL)animate
263 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
264 [invoc setTarget: self];
266 if (![self isVisible] || [self alphaValue] == 0.0)
272 [self orderOut: self animate: YES callback: invoc];
275 - (void)orderOut: (id)sender animate: (BOOL)animate
277 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
278 [invoc setTarget: self];
279 [invoc setArgument: sender atIndex: 0];
280 [self orderOut: sender animate: animate callback: invoc];
283 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
285 NSViewAnimation *anim;
286 NSViewAnimation *current_anim;
287 NSMutableDictionary *dict;
291 [self orderOut: sender];
295 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
297 [dict setObject:self forKey:NSViewAnimationTargetKey];
299 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
300 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
303 [anim setAnimationBlockingMode:NSAnimationNonblocking];
304 [anim setDuration:0.9];
305 [anim setFrameRate:30];
306 [anim setUserInfo: callback];
308 @synchronized(self) {
309 current_anim = self->animation;
311 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
319 [current_anim stopAnimation];
320 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
321 [current_anim release];
324 [anim setCurrentProgress:1.0 - [self alphaValue]];
325 self->animation = anim;
326 [self setDelegate: self];
327 [anim startAnimation];
332 - (void)orderFront: (id)sender animate: (BOOL)animate
334 NSViewAnimation *anim;
335 NSViewAnimation *current_anim;
336 NSMutableDictionary *dict;
340 [super orderFront: sender];
341 [self setAlphaValue: 1.0];
345 if (![self isVisible])
347 [self setAlphaValue: 0.0];
348 [super orderFront: sender];
350 else if ([self alphaValue] == 1.0)
352 [super orderFront: self];
356 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
358 [dict setObject:self forKey:NSViewAnimationTargetKey];
360 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
361 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
364 [anim setAnimationBlockingMode:NSAnimationNonblocking];
365 [anim setDuration:0.5];
366 [anim setFrameRate:30];
368 @synchronized(self) {
369 current_anim = self->animation;
371 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
379 [current_anim stopAnimation];
380 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
381 [current_anim release];
384 [anim setCurrentProgress:[self alphaValue]];
385 self->animation = anim;
386 [self setDelegate: self];
387 [self orderFront: sender];
388 [anim startAnimation];
393 - (void)animationDidEnd:(NSAnimation*)anim
395 if ([self alphaValue] <= 0.0)
397 NSInvocation * invoc;
398 [super orderOut: nil];
399 [self setAlphaValue: 1.0];
400 if ((invoc = [anim userInfo]))
406 /*****************************************************************************
407 * VLCControllerWindow
408 *****************************************************************************/
410 @implementation VLCControllerWindow
412 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
413 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
415 /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts
416 * needs some further investigation! -- feepk
417 BOOL b_useTextured = YES;
419 if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] )
422 styleMask ^= NSTexturedBackgroundWindowMask;
425 self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
426 backing:backingType defer:flag];
428 [[VLCMain sharedInstance] updateTogglePlaylistState];
430 /* FIXME: see above...
433 [self setContentBorderThickness:28.0 forEdge:NSMinYEdge];
437 /* we want to be moveable regardless of our style */
438 [self setMovableByWindowBackground: YES];
440 /* we don't want this window to be restored on relaunch */
441 if ([self respondsToSelector:@selector(setRestorable:)])
442 [self setRestorable:NO];
447 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
449 /* We indeed want to prioritize Cocoa key equivalent against libvlc,
450 so we perform the menu equivalent now. */
451 if([[NSApp mainMenu] performKeyEquivalent:o_event])
454 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
455 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
462 /*****************************************************************************
464 *****************************************************************************/
466 @implementation VLCControllerView
470 [self unregisterDraggedTypes];
476 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
477 NSFilenamesPboardType, nil]];
480 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
482 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
483 == NSDragOperationGeneric)
485 return NSDragOperationGeneric;
489 return NSDragOperationNone;
493 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
498 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
500 NSPasteboard *o_paste = [sender draggingPasteboard];
501 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
502 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
503 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
507 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
510 NSArray *o_array = [NSArray array];
511 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
512 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
514 for( i = 0; i < (int)[o_values count]; i++)
517 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
521 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
524 o_array = [o_array arrayByAddingObject: o_dic];
526 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
530 [self setNeedsDisplay:YES];
534 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
536 [self setNeedsDisplay:YES];
541 /*****************************************************************************
542 * VLBrushedMetalImageView
543 *****************************************************************************/
545 @implementation VLBrushedMetalImageView
547 - (BOOL)mouseDownCanMoveWindow
554 [self unregisterDraggedTypes];
560 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
561 NSFilenamesPboardType, nil]];
564 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
566 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
567 == NSDragOperationGeneric)
569 return NSDragOperationGeneric;
573 return NSDragOperationNone;
577 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
582 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
584 NSPasteboard *o_paste = [sender draggingPasteboard];
585 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
586 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
587 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
588 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
592 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
595 NSArray *o_array = [NSArray array];
596 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
597 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
599 for( i = 0; i < (int)[o_values count]; i++)
602 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
606 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
609 o_array = [o_array arrayByAddingObject: o_dic];
612 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
614 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
618 [self setNeedsDisplay:YES];
622 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
624 [self setNeedsDisplay:YES];
630 /*****************************************************************************
632 *****************************************************************************/
633 @implementation MPSlider
635 void _drawKnobInRect(NSRect knobRect)
637 // Center knob in given rect
638 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
639 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
642 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
643 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
644 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
645 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
646 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
647 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
648 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
651 void _drawFrameInRect(NSRect frameRect)
654 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
655 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
656 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
657 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
660 - (void)drawRect:(NSRect)rect
662 // Draw default to make sure the slider behaves correctly
663 [[NSGraphicsContext currentContext] saveGraphicsState];
664 NSRectClip(NSZeroRect);
665 [super drawRect:rect];
666 [[NSGraphicsContext currentContext] restoreGraphicsState];
669 rect = [self bounds];
670 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
671 rect.origin.x += diff-1;
672 rect.origin.y += diff;
673 rect.size.width -= 2*diff-2;
674 rect.size.height -= 2*diff;
677 NSRect knobRect = [[self cell] knobRectFlipped:NO];
678 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
679 _drawFrameInRect(rect);
680 _drawKnobInRect(knobRect);
683 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
688 _drawFrameInRect(rect);
689 _drawKnobInRect(knobRect);
694 /*****************************************************************************
696 *****************************************************************************/
698 @implementation TimeLineSlider
700 - (void)drawKnobInRect:(NSRect)knobRect
703 NSImage *img = [NSImage imageNamed:@"progression-knob"];
704 image_rect.size = [img size];
705 image_rect.origin.x = 0;
706 image_rect.origin.y = 0;
707 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
708 knobRect.size.width = image_rect.size.width;
709 knobRect.size.height = image_rect.size.height;
710 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
713 - (void)drawRect:(NSRect)rect
715 /* Draw default to make sure the slider behaves correctly */
716 [[NSGraphicsContext currentContext] saveGraphicsState];
717 NSRectClip(NSZeroRect);
718 [super drawRect:rect];
719 [[NSGraphicsContext currentContext] restoreGraphicsState];
721 NSRect knobRect = [[self cell] knobRectFlipped:NO];
722 knobRect.origin.y+=1;
723 [self drawKnobInRect: knobRect];
728 /*****************************************************************************
730 *****************************************************************************/
732 @implementation ITSlider
734 - (void)drawKnobInRect:(NSRect)knobRect
737 NSImage *img = [NSImage imageNamed:@"volume-slider-knob"];
738 image_rect.size = [img size];
739 image_rect.origin.x = 0;
740 image_rect.origin.y = 0;
741 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
742 knobRect.size.width = image_rect.size.width;
743 knobRect.size.height = image_rect.size.height;
744 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
747 - (void)drawRect:(NSRect)rect
749 /* Draw default to make sure the slider behaves correctly */
750 [[NSGraphicsContext currentContext] saveGraphicsState];
751 NSRectClip(NSZeroRect);
752 [super drawRect:rect];
753 [[NSGraphicsContext currentContext] restoreGraphicsState];
755 NSRect knobRect = [[self cell] knobRectFlipped:NO];
756 knobRect.origin.y+=2;
757 [self drawKnobInRect: knobRect];
762 /*****************************************************************************
763 * VLCTimeField implementation
764 *****************************************************************************
765 * we need this to catch our click-event in the controller window
766 *****************************************************************************/
768 @implementation VLCTimeField
769 - (void)mouseDown: (NSEvent *)ourEvent
771 if( [ourEvent clickCount] > 1 )
772 [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
774 [[VLCMain sharedInstance] timeFieldWasClicked: self];