1 /*****************************************************************************
2 * misc.m: code not specific to vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2007 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #include <Cocoa/Cocoa.h>
26 #include "intf.h" /* VLCApplication */
31 /*****************************************************************************
32 * NSAnimation (VLCAdditions)
34 * Missing extension to NSAnimation
35 *****************************************************************************/
37 @implementation NSAnimation (VLCAdditions)
38 /* fake class attributes */
39 static NSMapTable *VLCAdditions_userInfo = NULL;
43 /* init our fake object attribute */
44 VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
49 NSMapRemove(VLCAdditions_userInfo, self);
53 - (void)setUserInfo: (void *)userInfo
55 NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
60 return NSMapGet(VLCAdditions_userInfo, self);
64 /*****************************************************************************
65 * NSScreen (VLCAdditions)
67 * Missing extension to NSScreen
68 *****************************************************************************/
70 @implementation NSScreen (VLCAdditions)
72 static NSMutableArray *blackoutWindows = NULL;
76 /* init our fake object attribute */
77 blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
80 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
84 for( i = 0; i < [[NSScreen screens] count]; i++ )
86 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
87 if([screen displayID] == displayID)
95 return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
98 - (BOOL)isScreen: (NSScreen*)screen
100 return ([self displayID] == [screen displayID]);
103 - (CGDirectDisplayID)displayID
105 return (CGDirectDisplayID)_screenNumber;
108 - (void)blackoutOtherScreens
112 /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
113 [blackoutWindows makeObjectsPerformSelector:@selector(close)];
114 [blackoutWindows removeAllObjects];
117 for(i = 0; i < [[NSScreen screens] count]; i++)
119 NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
120 VLCWindow *blackoutWindow;
123 if([self isScreen: screen])
126 screen_rect = [screen frame];
127 screen_rect.origin.x = screen_rect.origin.y = 0.0f;
129 /* blackoutWindow alloc strategy
130 - The NSMutableArray blackoutWindows has the blackoutWindow references
131 - blackoutOtherDisplays is responsible for alloc/releasing its Windows
133 blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
134 backing: NSBackingStoreBuffered defer: NO screen: screen];
135 [blackoutWindow setBackgroundColor:[NSColor blackColor]];
136 [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
138 [blackoutWindow orderFront: self animate: YES];
140 [blackoutWindows addObject: blackoutWindow];
141 [blackoutWindow release];
145 + (void)unblackoutScreens
149 for(i = 0; i < [blackoutWindows count]; i++)
151 VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
152 [blackoutWindow closeAndAnimate: YES];
158 /*****************************************************************************
161 * Missing extension to NSWindow
162 *****************************************************************************/
164 @implementation VLCWindow
165 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
166 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
168 self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
170 b_isset_canBecomeKeyWindow = NO;
173 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
175 b_isset_canBecomeKeyWindow = YES;
176 b_canBecomeKeyWindow = canBecomeKey;
179 - (BOOL)canBecomeKeyWindow
181 if(b_isset_canBecomeKeyWindow)
182 return b_canBecomeKeyWindow;
184 return [super canBecomeKeyWindow];
187 - (void)closeAndAnimate: (BOOL)animate
191 if (!animate || MACOS_VERSION < 10.4f)
197 invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
198 [invoc setTarget: (id)super];
200 if (![self isVisible] || [self alphaValue] == 0.0)
206 [self orderOut: self animate: YES callback: invoc];
209 - (void)orderOut: (id)sender animate: (BOOL)animate
211 NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
212 [invoc setTarget: (id)super];
213 [invoc setArgument: sender atIndex: 0];
214 [self orderOut: sender animate: animate callback: invoc];
217 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
219 NSViewAnimation *anim;
220 NSViewAnimation *current_anim;
221 NSMutableDictionary *dict;
223 if (!animate || MACOS_VERSION < 10.4f)
225 [self orderOut: sender];
229 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
231 [dict setObject:self forKey:NSViewAnimationTargetKey];
233 [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
234 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
237 [anim setAnimationBlockingMode:NSAnimationNonblocking];
238 [anim setDuration:0.9];
239 [anim setFrameRate:30];
240 [anim setUserInfo: callback];
242 @synchronized(self) {
243 current_anim = self->animation;
245 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
253 [current_anim stopAnimation];
254 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
255 [current_anim release];
258 [anim setCurrentProgress:1.0 - [self alphaValue]];
259 self->animation = anim;
260 [self setDelegate: self];
261 [anim startAnimation];
266 - (void)orderFront: (id)sender animate: (BOOL)animate
268 NSViewAnimation *anim;
269 NSViewAnimation *current_anim;
270 NSMutableDictionary *dict;
272 if (!animate || MACOS_VERSION < 10.4f)
274 [super orderFront: sender];
275 [self setAlphaValue: 1.0];
279 if (![self isVisible])
281 [self setAlphaValue: 0.0];
282 [super orderFront: sender];
284 else if ([self alphaValue] == 1.0)
286 [super orderFront: self];
290 dict = [[NSMutableDictionary alloc] initWithCapacity:2];
292 [dict setObject:self forKey:NSViewAnimationTargetKey];
294 [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
295 anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
298 [anim setAnimationBlockingMode:NSAnimationNonblocking];
299 [anim setDuration:0.5];
300 [anim setFrameRate:30];
302 @synchronized(self) {
303 current_anim = self->animation;
305 if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
313 [current_anim stopAnimation];
314 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
315 [current_anim release];
318 [anim setCurrentProgress:[self alphaValue]];
319 self->animation = anim;
320 [self setDelegate: self];
321 [self orderFront: sender];
322 [anim startAnimation];
327 - (void)animationDidEnd:(NSAnimation*)anim
329 if ([self alphaValue] <= 0.0)
331 NSInvocation * invoc;
332 [super orderOut: nil];
333 [self setAlphaValue: 1.0];
334 if ((invoc = [anim userInfo]))
340 /*****************************************************************************
341 * VLCControllerWindow
342 *****************************************************************************/
344 @implementation VLCControllerWindow
346 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
347 backing:(NSBackingStoreType)backingType defer:(BOOL)flag
349 self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
350 backing:backingType defer:flag];
352 [[VLCMain sharedInstance] updateTogglePlaylistState];
357 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
359 /* We indeed want to prioritize Cocoa key equivalent against libvlc,
360 so we perform the menu equivalent now. */
361 if([[NSApp mainMenu] performKeyEquivalent:o_event])
364 return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] ||
365 [(VLCControls *)[[VLCMain sharedInstance] getControls] keyEvent:o_event];
372 /*****************************************************************************
374 *****************************************************************************/
376 @implementation VLCControllerView
380 [self unregisterDraggedTypes];
387 /* dealloc doesn't get called on 10.5 if GC is enabled, so we need to provide the basic functionality here */
388 [self unregisterDraggedTypes];
395 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
396 NSFilenamesPboardType, nil]];
399 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
401 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
402 == NSDragOperationGeneric)
404 return NSDragOperationGeneric;
408 return NSDragOperationNone;
412 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
417 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
419 NSPasteboard *o_paste = [sender draggingPasteboard];
420 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
421 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
422 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
426 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
429 NSArray *o_array = [NSArray array];
430 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
431 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
433 for( i = 0; i < (int)[o_values count]; i++)
436 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
437 o_array = [o_array arrayByAddingObject: o_dic];
439 [(VLCPlaylist *)[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
443 [self setNeedsDisplay:YES];
447 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
449 [self setNeedsDisplay:YES];
454 /*****************************************************************************
455 * VLBrushedMetalImageView
456 *****************************************************************************/
458 @implementation VLBrushedMetalImageView
460 - (BOOL)mouseDownCanMoveWindow
467 [self unregisterDraggedTypes];
474 /* dealloc doesn't get called on 10.5 if GC is enabled, so we need to provide the basic functionality here */
475 [self unregisterDraggedTypes];
482 [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
483 NSFilenamesPboardType, nil]];
486 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
488 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
489 == NSDragOperationGeneric)
491 return NSDragOperationGeneric;
495 return NSDragOperationNone;
499 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
504 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
506 NSPasteboard *o_paste = [sender draggingPasteboard];
507 NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
508 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
509 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
510 BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
514 if ([o_desired_type isEqualToString:NSFilenamesPboardType])
517 NSArray *o_array = [NSArray array];
518 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
519 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
521 for( i = 0; i < (int)[o_values count]; i++)
524 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
525 o_array = [o_array arrayByAddingObject: o_dic];
528 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:NO];
530 [[[VLCMain sharedInstance] getPlaylist] appendArray: o_array atPos: -1 enqueue:YES];
534 [self setNeedsDisplay:YES];
538 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
540 [self setNeedsDisplay:YES];
546 /*****************************************************************************
548 *****************************************************************************/
549 @implementation MPSlider
551 void _drawKnobInRect(NSRect knobRect)
553 // Center knob in given rect
554 knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
555 knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
558 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
559 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
560 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
561 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
562 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
563 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
564 NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
567 void _drawFrameInRect(NSRect frameRect)
570 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
571 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
572 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
573 NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
576 - (void)drawRect:(NSRect)rect
578 // Draw default to make sure the slider behaves correctly
579 [[NSGraphicsContext currentContext] saveGraphicsState];
580 NSRectClip(NSZeroRect);
581 [super drawRect:rect];
582 [[NSGraphicsContext currentContext] restoreGraphicsState];
585 rect = [self bounds];
586 int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
587 rect.origin.x += diff-1;
588 rect.origin.y += diff;
589 rect.size.width -= 2*diff-2;
590 rect.size.height -= 2*diff;
593 NSRect knobRect = [[self cell] knobRectFlipped:NO];
594 [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
595 _drawFrameInRect(rect);
596 _drawKnobInRect(knobRect);
599 [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
604 _drawFrameInRect(rect);
605 _drawKnobInRect(knobRect);
611 /*****************************************************************************
613 *****************************************************************************/
615 @implementation ITSlider
619 if ([[self cell] class] != [ITSliderCell class]) {
621 NSSliderCell *oldCell = [self cell];
622 NSSliderCell *newCell = [[[ITSliderCell alloc] init] autorelease];
623 [newCell setTag:[oldCell tag]];
624 [newCell setTarget:[oldCell target]];
625 [newCell setAction:[oldCell action]];
626 [newCell setControlSize:[oldCell controlSize]];
627 [newCell setType:[oldCell type]];
628 [newCell setState:[oldCell state]];
629 [newCell setAllowsTickMarkValuesOnly:[oldCell allowsTickMarkValuesOnly]];
630 [newCell setAltIncrementValue:[oldCell altIncrementValue]];
631 [newCell setControlTint:[oldCell controlTint]];
632 [newCell setKnobThickness:[oldCell knobThickness]];
633 [newCell setMaxValue:[oldCell maxValue]];
634 [newCell setMinValue:[oldCell minValue]];
635 [newCell setDoubleValue:[oldCell doubleValue]];
636 [newCell setNumberOfTickMarks:[oldCell numberOfTickMarks]];
637 [newCell setEditable:[oldCell isEditable]];
638 [newCell setEnabled:[oldCell isEnabled]];
639 [newCell setEntryType:[oldCell entryType]];
640 [newCell setHighlighted:[oldCell isHighlighted]];
641 [newCell setTickMarkPosition:[oldCell tickMarkPosition]];
642 [self setCell:newCell];
648 /*****************************************************************************
650 *****************************************************************************/
651 @implementation ITSliderCell
656 _knobOff = [[NSImage imageNamed:@"volumeslider_normal"] retain];
657 _knobOn = [[NSImage imageNamed:@"volumeslider_blue"] retain];
658 b_mouse_down = FALSE;
669 - (void)drawKnob:(NSRect)knob_rect
678 [[self controlView] lockFocus];
679 [knob compositeToPoint:NSMakePoint( knob_rect.origin.x + 1,
680 knob_rect.origin.y + knob_rect.size.height -2 )
681 operation:NSCompositeSourceOver];
682 [[self controlView] unlockFocus];
685 - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:
686 (NSView *)controlView mouseIsUp:(BOOL)flag
690 [super stopTracking:lastPoint at:stopPoint inView:controlView mouseIsUp:flag];
693 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView
697 return [super startTrackingAt:startPoint inView:controlView];