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 */
29 #import "MainWindow.h"
35 /*****************************************************************************
36 * NSAnimation (VLCAdditions)
38 * Missing extension to NSAnimation
39 *****************************************************************************/
41 @implementation NSAnimation (VLCAdditions)
42 /* fake class attributes */
43 static NSMapTable *VLCAdditions_userInfo = NULL;
47 /* init our fake object attribute */
48 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
53 NSMapRemove(VLCAdditions_userInfo, self);
57 - (void)setUserInfo: (void *)userInfo
59 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
64 return NSMapGet(VLCAdditions_userInfo, self);
68 /*****************************************************************************
69 * NSScreen (VLCAdditions)
71 * Missing extension to NSScreen
72 *****************************************************************************/
74 @implementation NSScreen (VLCAdditions)
76 static NSMutableArray *blackoutWindows = NULL;
80 /* init our fake object attribute */
81 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
84 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
88 for( i = 0; i < [[NSScreen screens] count]; i++ )
90 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
91 if([screen displayID] == displayID)
99 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
102 - (BOOL)isScreen: (NSScreen*)screen
104 return ([self displayID] == [screen displayID]);
107 - (CGDirectDisplayID)displayID
109 return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
112 - (void)blackoutOtherScreens
116 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
117 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
118 [blackoutWindows removeAllObjects];
120 for(i = 0; i < [[NSScreen screens] count]; 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 ] )
148 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
152 + (void)unblackoutScreens
156 for(i = 0; i < [blackoutWindows count]; i++)
158 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
159 [blackoutWindow closeAndAnimate: YES];
162 SetSystemUIMode( kUIModeNormal, 0);
167 /*****************************************************************************
170 * Missing extension to NSWindow
171 *****************************************************************************/
173 @implementation VLCWindow
174 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
175 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
177 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
180 b_isset_canBecomeKeyWindow = NO;
181 /* we don't want this window to be restored on relaunch */
182 if ([self respondsToSelector:@selector(setRestorable:)])
183 [self setRestorable:NO];
187 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
189 b_isset_canBecomeKeyWindow = YES;
190 b_canBecomeKeyWindow = canBecomeKey;
193 - (BOOL)canBecomeKeyWindow
195 if(b_isset_canBecomeKeyWindow)
196 return b_canBecomeKeyWindow;
198 return [super canBecomeKeyWindow];
201 - (void)closeAndAnimate: (BOOL)animate
211 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
212 [invoc setTarget: self];
214 if (![self isVisible] || [self alphaValue] == 0.0)
220 [self orderOut: self animate: YES callback: invoc];
223 - (void)orderOut: (id)sender animate: (BOOL)animate
225 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
226 [invoc setTarget: self];
227 [invoc setArgument: sender atIndex: 0];
228 [self orderOut: sender animate: animate callback: invoc];
231 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
233 NSViewAnimation *anim;
234 NSViewAnimation *current_anim;
235 NSMutableDictionary *dict;
239 [self orderOut: sender];
243 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
245 [dict setObject:self forKey:NSViewAnimationTargetKey];
247 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
248 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
251 [anim setAnimationBlockingMode:NSAnimationNonblocking];
252 [anim setDuration:0.9];
253 [anim setFrameRate:30];
254 [anim setUserInfo: callback];
256 @synchronized(self) {
257 current_anim = self->animation;
259 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
267 [current_anim stopAnimation];
268 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
269 [current_anim release];
272 [anim setCurrentProgress:1.0 - [self alphaValue]];
273 self->animation = anim;
274 [self setDelegate: self];
275 [anim startAnimation];
280 - (void)orderFront: (id)sender animate: (BOOL)animate
282 NSViewAnimation *anim;
283 NSViewAnimation *current_anim;
284 NSMutableDictionary *dict;
288 [super orderFront: sender];
289 [self setAlphaValue: 1.0];
293 if (![self isVisible])
295 [self setAlphaValue: 0.0];
296 [super orderFront: sender];
298 else if ([self alphaValue] == 1.0)
300 [super orderFront: self];
304 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
306 [dict setObject:self forKey:NSViewAnimationTargetKey];
308 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
309 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
312 [anim setAnimationBlockingMode:NSAnimationNonblocking];
313 [anim setDuration:0.5];
314 [anim setFrameRate:30];
316 @synchronized(self) {
317 current_anim = self->animation;
319 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
327 [current_anim stopAnimation];
328 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
329 [current_anim release];
332 [anim setCurrentProgress:[self alphaValue]];
333 self->animation = anim;
334 [self setDelegate: self];
335 [self orderFront: sender];
336 [anim startAnimation];
341 - (void)animationDidEnd:(NSAnimation*)anim
343 if ([self alphaValue] <= 0.0)
345 NSInvocation * invoc;
346 [super orderOut: nil];
347 [self setAlphaValue: 1.0];
348 if ((invoc = [anim userInfo]))
354 /*****************************************************************************
356 *****************************************************************************/
358 @implementation VLCControllerView
362 [self unregisterDraggedTypes];
368 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
369 NSFilenamesPboardType, nil]];
372 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
374 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
375 == NSDragOperationGeneric)
377 return NSDragOperationGeneric;
381 return NSDragOperationNone;
385 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
390 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
392 NSPasteboard *o_paste = [sender draggingPasteboard];
393 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
394 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
395 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
399 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
402 NSArray *o_array = [NSArray array];
403 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
404 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
406 for( i = 0; i < (int)[o_values count]; i++)
409 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
413 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
416 o_array = [o_array arrayByAddingObject: o_dic];
418 [(VLCPlaylist *)[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
422 [self setNeedsDisplay:YES];
426 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
428 [self setNeedsDisplay:YES];
433 /*****************************************************************************
434 * VLBrushedMetalImageView
435 *****************************************************************************/
437 @implementation VLBrushedMetalImageView
439 - (BOOL)mouseDownCanMoveWindow
446 [self unregisterDraggedTypes];
452 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
453 NSFilenamesPboardType, nil]];
456 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
458 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
459 == NSDragOperationGeneric)
461 return NSDragOperationGeneric;
465 return NSDragOperationNone;
469 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
474 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
476 NSPasteboard *o_paste = [sender draggingPasteboard];
477 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
478 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
479 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
480 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
484 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
487 NSArray *o_array = [NSArray array];
488 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
489 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
491 for( i = 0; i < (int)[o_values count]; i++)
494 char *psz_uri = make_URI([[o_values objectAtIndex:i] UTF8String], NULL);
498 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
501 o_array = [o_array arrayByAddingObject: o_dic];
504 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
506 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
510 [self setNeedsDisplay:YES];
514 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
516 [self setNeedsDisplay:YES];
522 /*****************************************************************************
524 *****************************************************************************/
525 @implementation MPSlider
527 void _drawKnobInRect(NSRect knobRect)
529 // Center knob in given rect
530 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
531 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
534 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
535 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
536 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
537 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
538 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
539 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
540 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
543 void _drawFrameInRect(NSRect frameRect)
546 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
547 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
548 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
549 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
552 - (void)drawRect:(NSRect)rect
554 // Draw default to make sure the slider behaves correctly
555 [[NSGraphicsContext currentContext] saveGraphicsState];
556 NSRectClip(NSZeroRect);
557 [super drawRect:rect];
558 [[NSGraphicsContext currentContext] restoreGraphicsState];
561 rect = [self bounds];
562 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
563 rect.origin.x += diff-1;
564 rect.origin.y += diff;
565 rect.size.width -= 2*diff-2;
566 rect.size.height -= 2*diff;
569 NSRect knobRect = [[self cell] knobRectFlipped:NO];
570 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
571 _drawFrameInRect(rect);
572 _drawKnobInRect(knobRect);
575 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
580 _drawFrameInRect(rect);
581 _drawKnobInRect(knobRect);
586 /*****************************************************************************
588 *****************************************************************************/
590 @implementation TimeLineSlider
592 - (void)drawKnobInRect:(NSRect)knobRect
595 NSImage *img = [NSImage imageNamed:@"progression-knob"];
596 image_rect.size = [img size];
597 image_rect.origin.x = 0;
598 image_rect.origin.y = 0;
599 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
600 knobRect.size.width = image_rect.size.width;
601 knobRect.size.height = image_rect.size.height;
602 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
605 - (void)drawRect:(NSRect)rect
607 /* Draw default to make sure the slider behaves correctly */
608 [[NSGraphicsContext currentContext] saveGraphicsState];
609 NSRectClip(NSZeroRect);
610 [super drawRect:rect];
611 [[NSGraphicsContext currentContext] restoreGraphicsState];
613 NSRect knobRect = [[self cell] knobRectFlipped:NO];
614 knobRect.origin.y+=1;
615 [self drawKnobInRect: knobRect];
620 /*****************************************************************************
622 *****************************************************************************/
624 @implementation ITSlider
626 - (void)drawKnobInRect:(NSRect)knobRect
629 NSImage *img = [NSImage imageNamed:@"volume-slider-knob"];
630 image_rect.size = [img size];
631 image_rect.origin.x = 0;
632 image_rect.origin.y = 0;
633 knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
634 knobRect.size.width = image_rect.size.width;
635 knobRect.size.height = image_rect.size.height;
636 [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
639 - (void)drawRect:(NSRect)rect
641 /* Draw default to make sure the slider behaves correctly */
642 [[NSGraphicsContext currentContext] saveGraphicsState];
643 NSRectClip(NSZeroRect);
644 [super drawRect:rect];
645 [[NSGraphicsContext currentContext] restoreGraphicsState];
647 NSRect knobRect = [[self cell] knobRectFlipped:NO];
648 knobRect.origin.y+=2;
649 [self drawKnobInRect: knobRect];
654 /*****************************************************************************
655 * VLCTimeField implementation
656 *****************************************************************************
657 * we need this to catch our click-event in the controller window
658 *****************************************************************************/
660 @implementation VLCTimeField
662 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
663 NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
665 [defaults registerDefaults:appDefaults];
668 - (void)mouseDown: (NSEvent *)ourEvent
670 if( [ourEvent clickCount] > 1 )
671 [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
674 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
675 [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
677 [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
681 - (BOOL)timeRemaining
683 return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];