1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2008 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 <QuickTime/QuickTime.h>
28 #import "intf.h" /* VLCApplication */
33 /*****************************************************************************
34 * NSAnimation (VLCAdditions)
36 * Missing extension to NSAnimation
37 *****************************************************************************/
39 @implementation NSAnimation (VLCAdditions)
40 /* fake class attributes */
41 static NSMapTable *VLCAdditions_userInfo = NULL;
45 /* init our fake object attribute */
46 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
51 NSMapRemove(VLCAdditions_userInfo, self);
55 - (void)setUserInfo: (void *)userInfo
57 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
62 return NSMapGet(VLCAdditions_userInfo, self);
66 /*****************************************************************************
67 * NSScreen (VLCAdditions)
69 * Missing extension to NSScreen
70 *****************************************************************************/
72 @implementation NSScreen (VLCAdditions)
74 static NSMutableArray *blackoutWindows = NULL;
78 /* init our fake object attribute */
79 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
82 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
86 for( i = 0; i < [[NSScreen screens] count]; i++ )
88 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
89 if([screen displayID] == displayID)
97 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
100 - (BOOL)isScreen: (NSScreen*)screen
102 return ([self displayID] == [screen displayID]);
105 - (CGDirectDisplayID)displayID
107 return (CGDirectDisplayID)_screenNumber;
110 - (void)blackoutOtherScreens
114 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
115 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
116 [blackoutWindows removeAllObjects];
118 for(i = 0; i < [[NSScreen screens] count]; i++)
120 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
121 VLCWindow *blackoutWindow;
124 if([self isScreen: screen])
127 screen_rect = [screen frame];
128 screen_rect.origin.x = screen_rect.origin.y = 0;
130 /* blackoutWindow alloc strategy
131 - The NSMutableArray blackoutWindows has the blackoutWindow references
132 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
134 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
135 backing: NSBackingStoreBuffered defer: NO screen: screen];
136 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
137 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
139 [blackoutWindow displayIfNeeded];
140 [blackoutWindow orderFront: self animate: YES];
142 [blackoutWindows addObject: blackoutWindow];
143 [blackoutWindow release];
145 if( [screen isMainScreen ] )
146 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
150 + (void)unblackoutScreens
154 for(i = 0; i < [blackoutWindows count]; i++)
156 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
157 [blackoutWindow closeAndAnimate: YES];
160 SetSystemUIMode( kUIModeNormal, 0);
165 /*****************************************************************************
168 * Missing extension to NSWindow
169 *****************************************************************************/
171 @implementation VLCWindow
172 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
173 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
175 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
177 b_isset_canBecomeKeyWindow = NO;
180 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
182 b_isset_canBecomeKeyWindow = YES;
183 b_canBecomeKeyWindow = canBecomeKey;
186 - (BOOL)canBecomeKeyWindow
188 if(b_isset_canBecomeKeyWindow)
189 return b_canBecomeKeyWindow;
191 return [super canBecomeKeyWindow];
194 - (void)closeAndAnimate: (BOOL)animate
198 if (!animate || MACOS_VERSION < 10.4f)
204 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
205 [invoc setTarget: (id)super];
207 if (![self isVisible] || [self alphaValue] == 0.0)
213 [self orderOut: self animate: YES callback: invoc];
216 - (void)orderOut: (id)sender animate: (BOOL)animate
218 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
219 [invoc setTarget: (id)super];
220 [invoc setArgument: sender atIndex: 0];
221 [self orderOut: sender animate: animate callback: invoc];
224 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
226 NSViewAnimation *anim;
227 NSViewAnimation *current_anim;
228 NSMutableDictionary *dict;
230 if (!animate || MACOS_VERSION < 10.4f)
232 [self orderOut: sender];
236 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
238 [dict setObject:self forKey:NSViewAnimationTargetKey];
240 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
241 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
244 [anim setAnimationBlockingMode:NSAnimationNonblocking];
245 [anim setDuration:0.9];
246 [anim setFrameRate:30];
247 [anim setUserInfo: callback];
249 @synchronized(self) {
250 current_anim = self->animation;
252 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
260 [current_anim stopAnimation];
261 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
262 [current_anim release];
265 [anim setCurrentProgress:1.0 - [self alphaValue]];
266 self->animation = anim;
267 [self setDelegate: self];
268 [anim startAnimation];
273 - (void)orderFront: (id)sender animate: (BOOL)animate
275 NSViewAnimation *anim;
276 NSViewAnimation *current_anim;
277 NSMutableDictionary *dict;
279 if (!animate || MACOS_VERSION < 10.4f)
281 [super orderFront: sender];
282 [self setAlphaValue: 1.0];
286 if (![self isVisible])
288 [self setAlphaValue: 0.0];
289 [super orderFront: sender];
291 else if ([self alphaValue] == 1.0)
293 [super orderFront: self];
297 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
299 [dict setObject:self forKey:NSViewAnimationTargetKey];
301 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
302 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
305 [anim setAnimationBlockingMode:NSAnimationNonblocking];
306 [anim setDuration:0.5];
307 [anim setFrameRate:30];
309 @synchronized(self) {
310 current_anim = self->animation;
312 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
320 [current_anim stopAnimation];
321 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
322 [current_anim release];
325 [anim setCurrentProgress:[self alphaValue]];
326 self->animation = anim;
327 [self setDelegate: self];
328 [self orderFront: sender];
329 [anim startAnimation];
334 - (void)animationDidEnd:(NSAnimation*)anim
336 if ([self alphaValue] <= 0.0)
338 NSInvocation * invoc;
339 [super orderOut: nil];
340 [self setAlphaValue: 1.0];
341 if ((invoc = [anim userInfo]))
347 /*****************************************************************************
348 * VLCControllerWindow
349 *****************************************************************************/
351 @implementation VLCControllerWindow
353 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
354 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
356 self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
357 backing:backingType defer:flag];
359 [[VLCMain sharedInstance] updateTogglePlaylistState];
364 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
366 /* We indeed want to prioritize Cocoa key equivalent against libvlc,
367 so we perform the menu equivalent now. */
368 if([[NSApp mainMenu] performKeyEquivalent:o_event])
371 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
372 [(VLCControls *)[[VLCMain sharedInstance] getControls] keyEvent:o_event];
379 /*****************************************************************************
381 *****************************************************************************/
383 @implementation VLCControllerView
387 [self unregisterDraggedTypes];
393 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
394 NSFilenamesPboardType, nil]];
397 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
399 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
400 == NSDragOperationGeneric)
402 return NSDragOperationGeneric;
406 return NSDragOperationNone;
410 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
415 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
417 NSPasteboard *o_paste = [sender draggingPasteboard];
418 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
419 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
420 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
424 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
427 NSArray *o_array = [NSArray array];
428 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
429 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
431 for( i = 0; i < (int)[o_values count]; i++)
434 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
435 o_array = [o_array arrayByAddingObject: o_dic];
437 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
441 [self setNeedsDisplay:YES];
445 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
447 [self setNeedsDisplay:YES];
452 /*****************************************************************************
453 * VLBrushedMetalImageView
454 *****************************************************************************/
456 @implementation VLBrushedMetalImageView
458 - (BOOL)mouseDownCanMoveWindow
465 [self unregisterDraggedTypes];
471 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
472 NSFilenamesPboardType, nil]];
475 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
477 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
478 == NSDragOperationGeneric)
480 return NSDragOperationGeneric;
484 return NSDragOperationNone;
488 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
493 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
495 NSPasteboard *o_paste = [sender draggingPasteboard];
496 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
497 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
498 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
499 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
503 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
506 NSArray *o_array = [NSArray array];
507 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
508 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
510 for( i = 0; i < (int)[o_values count]; i++)
513 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
514 o_array = [o_array arrayByAddingObject: o_dic];
517 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
519 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:YES];
523 [self setNeedsDisplay:YES];
527 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
529 [self setNeedsDisplay:YES];
535 /*****************************************************************************
537 *****************************************************************************/
538 @implementation MPSlider
540 void _drawKnobInRect(NSRect knobRect)
542 // Center knob in given rect
543 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
544 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
547 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
548 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
549 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
550 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
551 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
552 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
553 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
556 void _drawFrameInRect(NSRect frameRect)
559 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
560 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
561 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
562 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
565 - (void)drawRect:(NSRect)rect
567 // Draw default to make sure the slider behaves correctly
568 [[NSGraphicsContext currentContext] saveGraphicsState];
569 NSRectClip(NSZeroRect);
570 [super drawRect:rect];
571 [[NSGraphicsContext currentContext] restoreGraphicsState];
574 rect = [self bounds];
575 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
576 rect.origin.x += diff-1;
577 rect.origin.y += diff;
578 rect.size.width -= 2*diff-2;
579 rect.size.height -= 2*diff;
582 NSRect knobRect = [[self cell] knobRectFlipped:NO];
583 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
584 _drawFrameInRect(rect);
585 _drawKnobInRect(knobRect);
588 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
593 _drawFrameInRect(rect);
594 _drawKnobInRect(knobRect);
600 /*****************************************************************************
602 *****************************************************************************/
604 @implementation ITSlider
608 if ([[self cell] class] != [ITSliderCell class]) {
610 NSSliderCell *oldCell = [self cell];
611 NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
612 [newCell setTag:[oldCell tag]];
613 [newCell setTarget:[oldCell target]];
614 [newCell setAction:[oldCell action]];
615 [newCell setControlSize:[oldCell controlSize]];
616 [newCell setType:[oldCell type]];
617 [newCell setState:[oldCell state]];
618 [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
619 [newCell setAltIncrementValue:[oldCell altIncrementValue]];
620 [newCell setControlTint:[oldCell controlTint]];
621 [newCell setKnobThickness:[oldCell knobThickness]];
622 [newCell setMaxValue:[oldCell maxValue]];
623 [newCell setMinValue:[oldCell minValue]];
624 [newCell setDoubleValue:[oldCell doubleValue]];
625 [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
626 [newCell setEditable:[oldCell isEditable]];
627 [newCell setEnabled:[oldCell isEnabled]];
628 [newCell setEntryType:[oldCell entryType]];
629 [newCell setHighlighted:[oldCell isHighlighted]];
630 [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
631 [self setCell:newCell];
637 /*****************************************************************************
639 *****************************************************************************/
640 @implementation ITSliderCell
645 _knobOff = [NSImage imageNamed:@"volumeslider_normal"];
646 [self controlTintChanged];
647 [[NSNotificationCenter defaultCenter] addObserver: self
648 selector: @selector( controlTintChanged )
649 name: NSControlTintDidChangeNotification
651 b_mouse_down = FALSE;
655 - (void)controlTintChanged
657 if( [NSColor currentControlTint] == NSGraphiteControlTint )
658 _knobOn = [NSImage imageNamed:@"volumeslider_graphite"];
660 _knobOn = [NSImage imageNamed:@"volumeslider_blue"];
665 [[NSNotificationCenter defaultCenter] removeObserver: self];
671 - (void)drawKnob:(NSRect)knob_rect
680 [[self controlView] lockFocus];
681 [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
682 knob_rect.origin.y + knob_rect.size.height -2 )
683 operation:NSCompositeSourceOver];
684 [[self controlView] unlockFocus];
687 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
688 (NSView *)controlView mouseIsUp:(BOOL)flag
692 [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
695 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
699 return [super startTrackingAt:startPoint inView:controlView];