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];
135 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
139 for( i = 0; i < [[NSScreen screens] count]; i++ )
141 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
142 if([screen displayID] == displayID)
150 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
153 - (BOOL)isScreen: (NSScreen*)screen
155 return ([self displayID] == [screen displayID]);
158 - (CGDirectDisplayID)displayID
160 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
163 - (void)blackoutOtherScreens
167 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
168 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
169 [blackoutWindows removeAllObjects];
171 for(i = 0; i < [[NSScreen screens] count]; i++)
173 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
174 VLCWindow *blackoutWindow;
177 if([self isScreen: screen])
180 screen_rect = [screen frame];
181 screen_rect.origin.x = screen_rect.origin.y = 0;
183 /* blackoutWindow alloc strategy
184 - The NSMutableArray blackoutWindows has the blackoutWindow references
185 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
187 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
188 backing: NSBackingStoreBuffered defer: NO screen: screen];
189 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
190 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
192 [blackoutWindow displayIfNeeded];
193 [blackoutWindow orderFront: self animate: YES];
195 [blackoutWindows addObject: blackoutWindow];
196 [blackoutWindow release];
198 if( [screen isMainScreen ] )
199 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
203 + (void)unblackoutScreens
207 for(i = 0; i < [blackoutWindows count]; i++)
209 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
210 [blackoutWindow closeAndAnimate: YES];
213 SetSystemUIMode( kUIModeNormal, 0);
218 /*****************************************************************************
221 * Missing extension to NSWindow
222 *****************************************************************************/
224 @implementation VLCWindow
225 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
226 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
228 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
230 b_isset_canBecomeKeyWindow = NO;
233 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
235 b_isset_canBecomeKeyWindow = YES;
236 b_canBecomeKeyWindow = canBecomeKey;
239 - (BOOL)canBecomeKeyWindow
241 if(b_isset_canBecomeKeyWindow)
242 return b_canBecomeKeyWindow;
244 return [super canBecomeKeyWindow];
247 - (void)closeAndAnimate: (BOOL)animate
257 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
258 [invoc setTarget: (id)super];
260 if (![self isVisible] || [self alphaValue] == 0.0)
266 [self orderOut: self animate: YES callback: invoc];
269 - (void)orderOut: (id)sender animate: (BOOL)animate
271 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
272 [invoc setTarget: (id)super];
273 [invoc setArgument: sender atIndex: 0];
274 [self orderOut: sender animate: animate callback: invoc];
277 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
279 NSViewAnimation *anim;
280 NSViewAnimation *current_anim;
281 NSMutableDictionary *dict;
285 [self orderOut: sender];
289 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
291 [dict setObject:self forKey:NSViewAnimationTargetKey];
293 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
294 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
297 [anim setAnimationBlockingMode:NSAnimationNonblocking];
298 [anim setDuration:0.9];
299 [anim setFrameRate:30];
300 [anim setUserInfo: callback];
302 @synchronized(self) {
303 current_anim = self->animation;
305 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
313 [current_anim stopAnimation];
314 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
315 [current_anim release];
318 [anim setCurrentProgress:1.0 - [self alphaValue]];
319 self->animation = anim;
320 [self setDelegate: self];
321 [anim startAnimation];
326 - (void)orderFront: (id)sender animate: (BOOL)animate
328 NSViewAnimation *anim;
329 NSViewAnimation *current_anim;
330 NSMutableDictionary *dict;
334 [super orderFront: sender];
335 [self setAlphaValue: 1.0];
339 if (![self isVisible])
341 [self setAlphaValue: 0.0];
342 [super orderFront: sender];
344 else if ([self alphaValue] == 1.0)
346 [super orderFront: self];
350 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
352 [dict setObject:self forKey:NSViewAnimationTargetKey];
354 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
355 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
358 [anim setAnimationBlockingMode:NSAnimationNonblocking];
359 [anim setDuration:0.5];
360 [anim setFrameRate:30];
362 @synchronized(self) {
363 current_anim = self->animation;
365 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
373 [current_anim stopAnimation];
374 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
375 [current_anim release];
378 [anim setCurrentProgress:[self alphaValue]];
379 self->animation = anim;
380 [self setDelegate: self];
381 [self orderFront: sender];
382 [anim startAnimation];
387 - (void)animationDidEnd:(NSAnimation*)anim
389 if ([self alphaValue] <= 0.0)
391 NSInvocation * invoc;
392 [super orderOut: nil];
393 [self setAlphaValue: 1.0];
394 if ((invoc = [anim userInfo]))
400 /*****************************************************************************
401 * VLCControllerWindow
402 *****************************************************************************/
404 @implementation VLCControllerWindow
406 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
407 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
409 /* FIXME: this should enable the SnowLeopard window style, however, it leads to ugly artifacts
410 * needs some further investigation! -- feepk
411 BOOL b_useTextured = YES;
413 if( [[NSWindow class] instancesRespondToSelector:@selector(setContentBorderThickness:forEdge:)] )
416 styleMask ^= NSTexturedBackgroundWindowMask;
419 self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
420 backing:backingType defer:flag];
422 [[VLCMain sharedInstance] updateTogglePlaylistState];
424 /* FIXME: see above...
427 [self setContentBorderThickness:28.0 forEdge:NSMinYEdge];
433 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
435 /* We indeed want to prioritize Cocoa key equivalent against libvlc,
436 so we perform the menu equivalent now. */
437 if([[NSApp mainMenu] performKeyEquivalent:o_event])
440 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
441 [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
448 /*****************************************************************************
450 *****************************************************************************/
452 @implementation VLCControllerView
456 [self unregisterDraggedTypes];
462 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
463 NSFilenamesPboardType, nil]];
466 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
468 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
469 == NSDragOperationGeneric)
471 return NSDragOperationGeneric;
475 return NSDragOperationNone;
479 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
484 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
486 NSPasteboard *o_paste = [sender draggingPasteboard];
487 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
488 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
489 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
493 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
496 NSArray *o_array = [NSArray array];
497 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
498 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
500 for( i = 0; i < (int)[o_values count]; i++)
503 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
504 o_array = [o_array arrayByAddingObject: o_dic];
506 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
510 [self setNeedsDisplay:YES];
514 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
516 [self setNeedsDisplay:YES];
521 /*****************************************************************************
522 * VLBrushedMetalImageView
523 *****************************************************************************/
525 @implementation VLBrushedMetalImageView
527 - (BOOL)mouseDownCanMoveWindow
534 [self unregisterDraggedTypes];
540 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
541 NSFilenamesPboardType, nil]];
544 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
546 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
547 == NSDragOperationGeneric)
549 return NSDragOperationGeneric;
553 return NSDragOperationNone;
557 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
562 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
564 NSPasteboard *o_paste = [sender draggingPasteboard];
565 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
566 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
567 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
568 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
572 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
575 NSArray *o_array = [NSArray array];
576 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
577 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
579 for( i = 0; i < (int)[o_values count]; i++)
582 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
583 o_array = [o_array arrayByAddingObject: o_dic];
586 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
588 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
592 [self setNeedsDisplay:YES];
596 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
598 [self setNeedsDisplay:YES];
604 /*****************************************************************************
606 *****************************************************************************/
607 @implementation MPSlider
609 void _drawKnobInRect(NSRect knobRect)
611 // Center knob in given rect
612 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
613 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
616 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
617 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
618 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
619 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
620 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
621 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
622 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
625 void _drawFrameInRect(NSRect frameRect)
628 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
629 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
630 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
631 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
634 - (void)drawRect:(NSRect)rect
636 // Draw default to make sure the slider behaves correctly
637 [[NSGraphicsContext currentContext] saveGraphicsState];
638 NSRectClip(NSZeroRect);
639 [super drawRect:rect];
640 [[NSGraphicsContext currentContext] restoreGraphicsState];
643 rect = [self bounds];
644 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
645 rect.origin.x += diff-1;
646 rect.origin.y += diff;
647 rect.size.width -= 2*diff-2;
648 rect.size.height -= 2*diff;
651 NSRect knobRect = [[self cell] knobRectFlipped:NO];
652 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
653 _drawFrameInRect(rect);
654 _drawKnobInRect(knobRect);
657 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
662 _drawFrameInRect(rect);
663 _drawKnobInRect(knobRect);
669 /*****************************************************************************
671 *****************************************************************************/
673 @implementation ITSlider
677 if ([[self cell] class] != [ITSliderCell class]) {
679 NSSliderCell *oldCell = [self cell];
680 NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
681 [newCell setTag:[oldCell tag]];
682 [newCell setTarget:[oldCell target]];
683 [newCell setAction:[oldCell action]];
684 [newCell setControlSize:[oldCell controlSize]];
685 [newCell setType:[oldCell type]];
686 [newCell setState:[oldCell state]];
687 [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
688 [newCell setAltIncrementValue:[oldCell altIncrementValue]];
689 [newCell setControlTint:[oldCell controlTint]];
690 [newCell setKnobThickness:[oldCell knobThickness]];
691 [newCell setMaxValue:[oldCell maxValue]];
692 [newCell setMinValue:[oldCell minValue]];
693 [newCell setDoubleValue:[oldCell doubleValue]];
694 [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
695 [newCell setEditable:[oldCell isEditable]];
696 [newCell setEnabled:[oldCell isEnabled]];
697 [newCell setFormatter:[oldCell formatter]];
698 [newCell setHighlighted:[oldCell isHighlighted]];
699 [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
700 [self setCell:newCell];
706 /*****************************************************************************
708 *****************************************************************************/
709 @implementation ITSliderCell
714 _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
715 [self controlTintChanged];
716 [[NSNotificationCenter defaultCenter] addObserver: self
717 selector: @selector( controlTintChanged )
718 name: NSControlTintDidChangeNotification
720 b_mouse_down = FALSE;
724 - (void)controlTintChanged
726 if( [NSColor currentControlTint] == NSGraphiteControlTint )
727 _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
729 _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
734 [[NSNotificationCenter defaultCenter] removeObserver: self];
740 - (void)drawKnob:(NSRect)knob_rect
749 [[self controlView] lockFocus];
750 [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
751 knob_rect.origin.y + knob_rect.size.height -2 )
752 operation:NSCompositeSourceOver];
753 [[self controlView] unlockFocus];
756 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
757 (NSView *)controlView mouseIsUp:(BOOL)flag
761 [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
764 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
768 return [super startTrackingAt:startPoint inView:controlView];