1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2009 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 */
33 /*****************************************************************************
34 * NSImage (VLCAdditions)
37 *****************************************************************************/
38 @implementation NSImage (VLCAdditions)
39 + (id)imageWithSystemName:(int)name
41 /* ugly Carbon stuff following...
42 * regrettably, you can't get the icons through clean Cocoa */
44 /* retrieve our error icon */
48 returnValue = GetIconRef(kOnSystemDisk, 'macs', name, &ourIconRef);
49 icon = [[[NSImage alloc] initWithSize:NSMakeSize(32,32)] autorelease];
51 CGRect rect = CGRectMake(0,0,32,32);
52 PlotIconRefInContext((CGContextRef)[[NSGraphicsContext currentContext]
57 NULL /*inLabelColor*/,
58 kPlotIconRefNormalFlags,
61 returnValue = ReleaseIconRef(ourIconRef);
65 + (id)imageWithWarningIcon
67 static NSImage * imageWithWarningIcon = nil;
68 if( !imageWithWarningIcon )
70 imageWithWarningIcon = [[[self class] imageWithSystemName:'caut'] retain];
72 return imageWithWarningIcon;
75 + (id)imageWithErrorIcon
77 static NSImage * imageWithErrorIcon = nil;
78 if( !imageWithErrorIcon )
80 imageWithErrorIcon = [[[self class] imageWithSystemName:'stop'] retain];
82 return imageWithErrorIcon;
86 /*****************************************************************************
87 * NSAnimation (VLCAdditions)
89 * Missing extension to NSAnimation
90 *****************************************************************************/
92 @implementation NSAnimation (VLCAdditions)
93 /* fake class attributes */
94 static NSMapTable *VLCAdditions_userInfo = NULL;
98 /* init our fake object attribute */
99 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
104 NSMapRemove(VLCAdditions_userInfo, self);
108 - (void)setUserInfo: (void *)userInfo
110 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
115 return NSMapGet(VLCAdditions_userInfo, self);
119 /*****************************************************************************
120 * NSScreen (VLCAdditions)
122 * Missing extension to NSScreen
123 *****************************************************************************/
125 @implementation NSScreen (VLCAdditions)
127 static NSMutableArray *blackoutWindows = NULL;
131 /* init our fake object attribute */
132 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
137 [blackoutWindows removeAllObjects];
138 [blackoutWindows release];
142 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
146 for( i = 0; i < [[NSScreen screens] count]; i++ )
148 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
149 if([screen displayID] == displayID)
157 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
160 - (BOOL)isScreen: (NSScreen*)screen
162 return ([self displayID] == [screen displayID]);
165 - (CGDirectDisplayID)displayID
167 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
170 - (void)blackoutOtherScreens
174 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
175 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
176 [blackoutWindows removeAllObjects];
178 for(i = 0; i < [[NSScreen screens] count]; i++)
180 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
181 VLCWindow *blackoutWindow;
184 if([self isScreen: screen])
187 screen_rect = [screen frame];
188 screen_rect.origin.x = screen_rect.origin.y = 0;
190 /* blackoutWindow alloc strategy
191 - The NSMutableArray blackoutWindows has the blackoutWindow references
192 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
194 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
195 backing: NSBackingStoreBuffered defer: NO screen: screen];
196 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
197 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
199 [blackoutWindow displayIfNeeded];
200 [blackoutWindow orderFront: self animate: YES];
202 [blackoutWindows addObject: blackoutWindow];
203 [blackoutWindow release];
205 if( [screen isMainScreen ] )
206 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
210 + (void)unblackoutScreens
214 for(i = 0; i < [blackoutWindows count]; i++)
216 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
217 [blackoutWindow closeAndAnimate: YES];
220 SetSystemUIMode( kUIModeNormal, 0);
225 /*****************************************************************************
228 * Missing extension to NSWindow
229 *****************************************************************************/
231 @implementation VLCWindow
232 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
233 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
235 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
237 b_isset_canBecomeKeyWindow = NO;
240 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
242 b_isset_canBecomeKeyWindow = YES;
243 b_canBecomeKeyWindow = canBecomeKey;
246 - (BOOL)canBecomeKeyWindow
248 if(b_isset_canBecomeKeyWindow)
249 return b_canBecomeKeyWindow;
251 return [super canBecomeKeyWindow];
254 - (void)closeAndAnimate: (BOOL)animate
264 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
265 [invoc setTarget: (id)super];
267 if (![self isVisible] || [self alphaValue] == 0.0)
273 [self orderOut: self animate: YES callback: invoc];
276 - (void)orderOut: (id)sender animate: (BOOL)animate
278 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
279 [invoc setTarget: (id)super];
280 [invoc setArgument: sender atIndex: 0];
281 [self orderOut: sender animate: animate callback: invoc];
284 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
286 NSViewAnimation *anim;
287 NSViewAnimation *current_anim;
288 NSMutableDictionary *dict;
292 [self orderOut: sender];
296 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
298 [dict setObject:self forKey:NSViewAnimationTargetKey];
300 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
301 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
304 [anim setAnimationBlockingMode:NSAnimationNonblocking];
305 [anim setDuration:0.9];
306 [anim setFrameRate:30];
307 [anim setUserInfo: callback];
309 @synchronized(self) {
310 current_anim = self->animation;
312 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
320 [current_anim stopAnimation];
321 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
322 [current_anim release];
325 [anim setCurrentProgress:1.0 - [self alphaValue]];
326 self->animation = anim;
327 [self setDelegate: self];
328 [anim startAnimation];
333 - (void)orderFront: (id)sender animate: (BOOL)animate
335 NSViewAnimation *anim;
336 NSViewAnimation *current_anim;
337 NSMutableDictionary *dict;
341 [super orderFront: sender];
342 [self setAlphaValue: 1.0];
346 if (![self isVisible])
348 [self setAlphaValue: 0.0];
349 [super orderFront: sender];
351 else if ([self alphaValue] == 1.0)
353 [super orderFront: self];
357 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
359 [dict setObject:self forKey:NSViewAnimationTargetKey];
361 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
362 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
365 [anim setAnimationBlockingMode:NSAnimationNonblocking];
366 [anim setDuration:0.5];
367 [anim setFrameRate:30];
369 @synchronized(self) {
370 current_anim = self->animation;
372 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
380 [current_anim stopAnimation];
381 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
382 [current_anim release];
385 [anim setCurrentProgress:[self alphaValue]];
386 self->animation = anim;
387 [self setDelegate: self];
388 [self orderFront: sender];
389 [anim startAnimation];
394 - (void)animationDidEnd:(NSAnimation*)anim
396 if ([self alphaValue] <= 0.0)
398 NSInvocation * invoc;
399 [super orderOut: nil];
400 [self setAlphaValue: 1.0];
401 if ((invoc = [anim userInfo]))
407 /*****************************************************************************
408 * VLCControllerWindow
409 *****************************************************************************/
411 @implementation VLCControllerWindow
413 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
414 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
416 /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts
417 * needs some further investigation! -- feepk
418 BOOL b_useTextured = YES;
420 if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] )
423 styleMask ^= NSTexturedBackgroundWindowMask;
426 self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
427 backing:backingType defer:flag];
429 [[VLCMain sharedInstance] updateTogglePlaylistState];
431 /* FIXME: see above...
434 [self setContentBorderThickness:28.0 forEdge:NSMinYEdge];
440 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
442 /* We indeed want to prioritize Cocoa key equivalent against libvlc,
443 so we perform the menu equivalent now. */
444 if([[NSApp mainMenu] performKeyEquivalent:o_event])
447 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
448 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
455 /*****************************************************************************
457 *****************************************************************************/
459 @implementation VLCControllerView
463 [self unregisterDraggedTypes];
469 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
470 NSFilenamesPboardType, nil]];
473 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
475 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
476 == NSDragOperationGeneric)
478 return NSDragOperationGeneric;
482 return NSDragOperationNone;
486 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
491 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
493 NSPasteboard *o_paste = [sender draggingPasteboard];
494 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
495 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
496 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
500 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
503 NSArray *o_array = [NSArray array];
504 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
505 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
507 for( i = 0; i < (int)[o_values count]; i++)
510 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
511 o_array = [o_array arrayByAddingObject: o_dic];
513 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
517 [self setNeedsDisplay:YES];
521 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
523 [self setNeedsDisplay:YES];
528 /*****************************************************************************
529 * VLBrushedMetalImageView
530 *****************************************************************************/
532 @implementation VLBrushedMetalImageView
534 - (BOOL)mouseDownCanMoveWindow
541 [self unregisterDraggedTypes];
547 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
548 NSFilenamesPboardType, nil]];
551 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
553 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
554 == NSDragOperationGeneric)
556 return NSDragOperationGeneric;
560 return NSDragOperationNone;
564 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
569 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
571 NSPasteboard *o_paste = [sender draggingPasteboard];
572 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
573 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
574 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
575 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
579 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
582 NSArray *o_array = [NSArray array];
583 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
584 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
586 for( i = 0; i < (int)[o_values count]; i++)
589 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
590 o_array = [o_array arrayByAddingObject: o_dic];
593 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
595 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
599 [self setNeedsDisplay:YES];
603 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
605 [self setNeedsDisplay:YES];
611 /*****************************************************************************
613 *****************************************************************************/
614 @implementation MPSlider
616 void _drawKnobInRect(NSRect knobRect)
618 // Center knob in given rect
619 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
620 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
623 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
624 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
625 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
626 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
627 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
628 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
629 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
632 void _drawFrameInRect(NSRect frameRect)
635 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
636 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
637 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
638 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
641 - (void)drawRect:(NSRect)rect
643 // Draw default to make sure the slider behaves correctly
644 [[NSGraphicsContext currentContext] saveGraphicsState];
645 NSRectClip(NSZeroRect);
646 [super drawRect:rect];
647 [[NSGraphicsContext currentContext] restoreGraphicsState];
650 rect = [self bounds];
651 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
652 rect.origin.x += diff-1;
653 rect.origin.y += diff;
654 rect.size.width -= 2*diff-2;
655 rect.size.height -= 2*diff;
658 NSRect knobRect = [[self cell] knobRectFlipped:NO];
659 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
660 _drawFrameInRect(rect);
661 _drawKnobInRect(knobRect);
664 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
669 _drawFrameInRect(rect);
670 _drawKnobInRect(knobRect);
676 /*****************************************************************************
678 *****************************************************************************/
680 @implementation ITSlider
684 if ([[self cell] class] != [ITSliderCell class]) {
686 NSSliderCell *oldCell = [self cell];
687 NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
688 [newCell setTag:[oldCell tag]];
689 [newCell setTarget:[oldCell target]];
690 [newCell setAction:[oldCell action]];
691 [newCell setControlSize:[oldCell controlSize]];
692 [newCell setType:[oldCell type]];
693 [newCell setState:[oldCell state]];
694 [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
695 [newCell setAltIncrementValue:[oldCell altIncrementValue]];
696 [newCell setControlTint:[oldCell controlTint]];
697 [newCell setKnobThickness:[oldCell knobThickness]];
698 [newCell setMaxValue:[oldCell maxValue]];
699 [newCell setMinValue:[oldCell minValue]];
700 [newCell setDoubleValue:[oldCell doubleValue]];
701 [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
702 [newCell setEditable:[oldCell isEditable]];
703 [newCell setEnabled:[oldCell isEnabled]];
704 [newCell setFormatter:[oldCell formatter]];
705 [newCell setHighlighted:[oldCell isHighlighted]];
706 [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
707 [self setCell:newCell];
713 /*****************************************************************************
715 *****************************************************************************/
716 @implementation ITSliderCell
721 _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
722 [self controlTintChanged];
723 [[NSNotificationCenter defaultCenter] addObserver: self
724 selector: @selector( controlTintChanged )
725 name: NSControlTintDidChangeNotification
727 b_mouse_down = FALSE;
731 - (void)controlTintChanged
733 if( [NSColor currentControlTint] == NSGraphiteControlTint )
734 _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
736 _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
741 [[NSNotificationCenter defaultCenter] removeObserver: self];
747 - (void)drawKnob:(NSRect)knob_rect
756 [[self controlView] lockFocus];
757 [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
758 knob_rect.origin.y + knob_rect.size.height -2 )
759 operation:NSCompositeSourceOver];
760 [[self controlView] unlockFocus];
763 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
764 (NSView *)controlView mouseIsUp:(BOOL)flag
768 [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
771 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
775 return [super startTrackingAt:startPoint inView:controlView];