]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
macosx: make sure that the fullscreen window is the main window (close #6850)
[vlc] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #import "misc.h"
26 #import "intf.h"                                          /* VLCApplication */
27 #import "MainWindow.h"
28 #import "controls.h"
29 #import "CoreInteraction.h"
30 #import <CoreAudio/CoreAudio.h>
31
32 /*****************************************************************************
33  * NSSound (VLCAdditions)
34  *
35  * added code to change the system volume, needed for the apple remote code
36  * this is simplified code, which won't let you set the exact volume
37  * (that's what the audio output is for after all), but just the system volume
38  * in steps of 1/16 (matching the default AR or volume key implementation).
39  *****************************************************************************/
40
41 @implementation NSSound (VLCAdditions)
42
43 + (float)systemVolumeForChannel:(int)channel
44 {
45     AudioDeviceID i_device;
46     float f_volume;
47     OSStatus err;
48     UInt32 i_size;
49
50     i_size = sizeof( i_device );
51     AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
52     err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
53     if (err != noErr)
54     {
55         msg_Warn( VLCIntf, "couldn't get main audio output device" );
56         return .0;
57     }
58
59     AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, channel };
60     i_size = sizeof( f_volume );
61     err = AudioObjectGetPropertyData(i_device, &propertyAddress, 0, NULL, &i_size, &f_volume);
62     if (err != noErr)
63     {
64         msg_Warn( VLCIntf, "couldn't get volume value" );
65         return .0;
66     }
67
68     return f_volume;
69 }
70
71 + (bool)setSystemVolume:(float)f_volume forChannel:(int)i_channel
72 {
73     /* the following code will fail on S/PDIF devices. there is an easy work-around, but we'd like to match the OS behavior */
74
75     AudioDeviceID i_device;
76     OSStatus err;
77     UInt32 i_size;
78     Boolean b_writeable;
79
80     i_size = sizeof( i_device );
81     AudioObjectPropertyAddress deviceAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioDevicePropertyScopeOutput, kAudioObjectPropertyElementMaster };
82     err = AudioObjectGetPropertyData( kAudioObjectSystemObject, &deviceAddress, 0, NULL, &i_size, &i_device );
83     if (err != noErr)
84     {
85         msg_Warn( VLCIntf, "couldn't get main audio output device" );
86         return NO;
87     }
88
89     AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, i_channel };
90     i_size = sizeof( f_volume );
91     err = AudioObjectIsPropertySettable( i_device, &propertyAddress, &b_writeable );
92     if (err != noErr || !b_writeable ) {
93         msg_Warn( VLCIntf, "we can't set the main audio devices' volume" );
94         return NO;
95     }
96     err = AudioObjectSetPropertyData(i_device, &propertyAddress, 0, NULL, i_size, &f_volume);
97
98     return YES;
99 }
100
101 + (void)increaseSystemVolume
102 {
103     float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
104     f_volume += .0625; // 1/16 to match the OS
105     bool b_returned = YES;
106
107     /* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
108     for (NSUInteger x = 1; b_returned ; x++)
109         b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
110 }
111
112 + (void)decreaseSystemVolume
113 {
114     float f_volume = [NSSound systemVolumeForChannel:1]; // we trust that mono is always available and that all channels got the same volume
115     f_volume -= .0625; // 1/16 to match the OS
116     bool b_returned = YES;
117
118     /* since core audio doesn't provide a reasonable way to see how many channels we got, let's see how long we can do this */
119     for (NSUInteger x = 1; b_returned ; x++)
120         b_returned = [NSSound setSystemVolume: f_volume forChannel:x];
121 }
122
123 @end
124
125 /*****************************************************************************
126  * NSAnimation (VLCAdditions)
127  *
128  *  Missing extension to NSAnimation
129  *****************************************************************************/
130
131 @implementation NSAnimation (VLCAdditions)
132 /* fake class attributes  */
133 static NSMapTable *VLCAdditions_userInfo = NULL;
134
135 + (void)load
136 {
137     /* init our fake object attribute */
138     VLCAdditions_userInfo = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
139 }
140
141 - (void)dealloc
142 {
143     NSMapRemove(VLCAdditions_userInfo, self);
144     [super dealloc];
145 }
146
147 - (void)setUserInfo: (void *)userInfo
148 {
149     NSMapInsert(VLCAdditions_userInfo, self, (void*)userInfo);
150 }
151
152 - (void *)userInfo
153 {
154     return NSMapGet(VLCAdditions_userInfo, self);
155 }
156 @end
157
158 /*****************************************************************************
159  * NSScreen (VLCAdditions)
160  *
161  *  Missing extension to NSScreen
162  *****************************************************************************/
163
164 @implementation NSScreen (VLCAdditions)
165
166 static NSMutableArray *blackoutWindows = NULL;
167
168 + (void)load
169 {
170     /* init our fake object attribute */
171     blackoutWindows = [[NSMutableArray alloc] initWithCapacity:1];
172 }
173
174 + (NSScreen *)screenWithDisplayID: (CGDirectDisplayID)displayID
175 {
176     NSUInteger count = [[NSScreen screens] count];
177
178     for( NSUInteger i = 0; i < count; i++ )
179     {
180         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
181         if([screen displayID] == displayID)
182             return screen;
183     }
184     return nil;
185 }
186
187 - (BOOL)isMainScreen
188 {
189     return ([self displayID] == [[[NSScreen screens] objectAtIndex:0] displayID]);
190 }
191
192 - (BOOL)isScreen: (NSScreen*)screen
193 {
194     return ([self displayID] == [screen displayID]);
195 }
196
197 - (CGDirectDisplayID)displayID
198 {
199     return (CGDirectDisplayID)[[[self deviceDescription] objectForKey: @"NSScreenNumber"] intValue];
200 }
201
202 - (void)blackoutOtherScreens
203 {
204     /* Free our previous blackout window (follow blackoutWindow alloc strategy) */
205     [blackoutWindows makeObjectsPerformSelector:@selector(close)];
206     [blackoutWindows removeAllObjects];
207
208     NSUInteger screenCount = [[NSScreen screens] count];
209     for(NSUInteger i = 0; i < screenCount; i++)
210     {
211         NSScreen *screen = [[NSScreen screens] objectAtIndex: i];
212         VLCWindow *blackoutWindow;
213         NSRect screen_rect;
214
215         if([self isScreen: screen])
216             continue;
217
218         screen_rect = [screen frame];
219         screen_rect.origin.x = screen_rect.origin.y = 0;
220
221         /* blackoutWindow alloc strategy
222             - The NSMutableArray blackoutWindows has the blackoutWindow references
223             - blackoutOtherDisplays is responsible for alloc/releasing its Windows
224         */
225         blackoutWindow = [[VLCWindow alloc] initWithContentRect: screen_rect styleMask: NSBorderlessWindowMask
226                 backing: NSBackingStoreBuffered defer: NO screen: screen];
227         [blackoutWindow setBackgroundColor:[NSColor blackColor]];
228         [blackoutWindow setLevel: NSFloatingWindowLevel]; /* Disappear when Expose is triggered */
229
230         [blackoutWindow displayIfNeeded];
231         [blackoutWindow orderFront: self animate: YES];
232
233         [blackoutWindows addObject: blackoutWindow];
234         [blackoutWindow release];
235
236         if( [screen isMainScreen] )
237         {
238             if (OSX_LEOPARD)
239                 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
240             else
241                 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
242         }
243     }
244 }
245
246 + (void)unblackoutScreens
247 {
248     NSUInteger blackoutWindowCount = [blackoutWindows count];
249
250     for(NSUInteger i = 0; i < blackoutWindowCount; i++)
251     {
252         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
253         [blackoutWindow closeAndAnimate: YES];
254     }
255
256     if (OSX_LEOPARD)
257         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
258     else
259         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
260 }
261
262 @end
263
264 /*****************************************************************************
265  * VLCWindow
266  *
267  *  Missing extension to NSWindow
268  *****************************************************************************/
269
270 @implementation VLCWindow
271 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
272     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
273 {
274     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
275     if( self )
276     {
277         b_isFullscreen = NO;
278         b_isset_canBecomeKeyWindow = NO;
279         /* we don't want this window to be restored on relaunch */
280         if (OSX_LION)
281             [self setRestorable:NO];
282     }
283     return self;
284 }
285
286 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
287 {
288     b_isset_canBecomeKeyWindow = YES;
289     b_canBecomeKeyWindow = canBecomeKey;
290 }
291
292 - (BOOL)canBecomeKeyWindow
293 {
294     if(b_isset_canBecomeKeyWindow)
295         return b_canBecomeKeyWindow;
296
297     return [super canBecomeKeyWindow];
298 }
299
300 - (void)setCanBecomeMainWindow: (BOOL)canBecomeMain
301 {
302     b_isset_canBecomeMainWindow = YES;
303     b_canBecomeMainWindow = canBecomeMain;
304 }
305
306 - (BOOL)canBecomeMainWindow
307 {
308     if(b_isset_canBecomeMainWindow)
309         return b_canBecomeMainWindow;
310
311     return [super canBecomeMainWindow];
312 }
313
314 - (void)closeAndAnimate: (BOOL)animate
315 {
316     NSInvocation *invoc;
317
318     if (!animate)
319     {
320         [super close];
321         return;
322     }
323
324     invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
325     [invoc setTarget: self];
326
327     if (![self isVisible] || [self alphaValue] == 0.0)
328     {
329         [super close];
330         return;
331     }
332
333     [self orderOut: self animate: YES callback: invoc];
334 }
335
336 - (void)orderOut: (id)sender animate: (BOOL)animate
337 {
338     NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
339     [invoc setTarget: self];
340     [invoc setArgument: sender atIndex: 0];
341     [self orderOut: sender animate: animate callback: invoc];
342 }
343
344 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
345 {
346     NSViewAnimation *anim;
347     NSViewAnimation *current_anim;
348     NSMutableDictionary *dict;
349
350     if (!animate)
351     {
352         [self orderOut: sender];
353         return;
354     }
355
356     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
357
358     [dict setObject:self forKey:NSViewAnimationTargetKey];
359
360     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
361     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
362     [dict release];
363
364     [anim setAnimationBlockingMode:NSAnimationNonblocking];
365     [anim setDuration:0.9];
366     [anim setFrameRate:30];
367     [anim setUserInfo: callback];
368
369     @synchronized(self) {
370         current_anim = self->animation;
371
372         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
373         {
374             [anim release];
375         }
376         else
377         {
378             if (current_anim)
379             {
380                 [current_anim stopAnimation];
381                 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
382                 [current_anim release];
383             }
384             else
385                 [anim setCurrentProgress:1.0 - [self alphaValue]];
386             self->animation = anim;
387             [self setDelegate: self];
388             [anim startAnimation];
389         }
390     }
391 }
392
393 - (void)orderFront: (id)sender animate: (BOOL)animate
394 {
395     NSViewAnimation *anim;
396     NSViewAnimation *current_anim;
397     NSMutableDictionary *dict;
398
399     if (!animate)
400     {
401         [super orderFront: sender];
402         [self setAlphaValue: 1.0];
403         return;
404     }
405
406     if (![self isVisible])
407     {
408         [self setAlphaValue: 0.0];
409         [super orderFront: sender];
410     }
411     else if ([self alphaValue] == 1.0)
412     {
413         [super orderFront: self];
414         return;
415     }
416
417     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
418
419     [dict setObject:self forKey:NSViewAnimationTargetKey];
420
421     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
422     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
423     [dict release];
424
425     [anim setAnimationBlockingMode:NSAnimationNonblocking];
426     [anim setDuration:0.5];
427     [anim setFrameRate:30];
428
429     @synchronized(self) {
430         current_anim = self->animation;
431
432         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
433         {
434             [anim release];
435         }
436         else
437         {
438             if (current_anim)
439             {
440                 [current_anim stopAnimation];
441                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
442                 [current_anim release];
443             }
444             else
445                 [anim setCurrentProgress:[self alphaValue]];
446             self->animation = anim;
447             [self setDelegate: self];
448             [self orderFront: sender];
449             [anim startAnimation];
450         }
451     }
452 }
453
454 - (void)animationDidEnd:(NSAnimation*)anim
455 {
456     if ([self alphaValue] <= 0.0)
457     {
458         NSInvocation * invoc;
459         [super orderOut: nil];
460         [self setAlphaValue: 1.0];
461         if ((invoc = [anim userInfo]))
462             [invoc invoke];
463     }
464 }
465
466 - (void)setFullscreen:(BOOL)b_var
467 {
468     b_isFullscreen = b_var;
469 }
470
471 - (BOOL)isFullscreen
472 {
473     return b_isFullscreen;
474 }
475
476 - (IBAction)fullscreen:(id)sender
477 {
478     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
479 }
480
481 @end
482
483 /*****************************************************************************
484  * VLBrushedMetalImageView
485  *****************************************************************************/
486
487 @implementation VLBrushedMetalImageView
488
489 - (BOOL)mouseDownCanMoveWindow
490 {
491     return YES;
492 }
493
494 - (void)dealloc
495 {
496     [self unregisterDraggedTypes];
497     [super dealloc];
498 }
499
500 - (void)awakeFromNib
501 {
502     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
503     [self setImageScaling: NSScaleToFit];
504     [self setImageFrameStyle: NSImageFrameNone];
505     [self setImageAlignment: NSImageAlignCenter];
506 }
507
508 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
509 {
510     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
511         return NSDragOperationGeneric;
512
513     return NSDragOperationNone;
514 }
515
516 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
517 {
518     return YES;
519 }
520
521 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
522 {
523     BOOL b_returned;
524     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
525
526     [self setNeedsDisplay:YES];
527     return b_returned;
528 }
529
530 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
531 {
532     [self setNeedsDisplay:YES];
533 }
534
535 @end
536
537
538 /*****************************************************************************
539  * MPSlider
540  *****************************************************************************/
541 @implementation MPSlider
542
543 void _drawKnobInRect(NSRect knobRect)
544 {
545     // Center knob in given rect
546     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
547     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
548
549     // Draw diamond
550     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
551     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
552     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
553     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
554     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
555     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
556     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
557 }
558
559 void _drawFrameInRect(NSRect frameRect)
560 {
561     // Draw frame
562     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
563     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
564     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
565     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
566 }
567
568 - (void)drawRect:(NSRect)rect
569 {
570     // Draw default to make sure the slider behaves correctly
571     [[NSGraphicsContext currentContext] saveGraphicsState];
572     NSRectClip(NSZeroRect);
573     [super drawRect:rect];
574     [[NSGraphicsContext currentContext] restoreGraphicsState];
575
576     // Full size
577     rect = [self bounds];
578     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
579     rect.origin.x += diff-1;
580     rect.origin.y += diff;
581     rect.size.width -= 2*diff-2;
582     rect.size.height -= 2*diff;
583
584     // Draw dark
585     NSRect knobRect = [[self cell] knobRectFlipped:NO];
586     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
587     _drawFrameInRect(rect);
588     _drawKnobInRect(knobRect);
589
590     // Draw shadow
591     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
592     rect.origin.x++;
593     rect.origin.y++;
594     knobRect.origin.x++;
595     knobRect.origin.y++;
596     _drawFrameInRect(rect);
597     _drawKnobInRect(knobRect);
598 }
599
600 @end
601
602 /*****************************************************************************
603  * TimeLineSlider
604  *****************************************************************************/
605
606 @implementation TimeLineSlider
607
608 - (void)awakeFromNib
609 {
610     if (config_GetInt( VLCIntf, "macosx-interfacestyle" ))
611         o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
612     else
613         o_knob_img = [NSImage imageNamed:@"progression-knob"];
614     img_rect.size = [o_knob_img size];
615     img_rect.origin.x = img_rect.origin.y = 0;
616 }
617
618 - (void)dealloc
619 {
620     [o_knob_img release];
621     [super dealloc];
622 }
623
624 - (CGFloat)knobPosition
625 {
626     NSRect knobRect = [[self cell] knobRectFlipped:NO];
627     knobRect.origin.x += knobRect.size.width / 2;
628     return knobRect.origin.x;
629 }
630
631 - (void)drawKnobInRect:(NSRect)knobRect
632 {
633     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
634     knobRect.size.width = img_rect.size.width;
635     knobRect.size.height = img_rect.size.height;
636     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
637 }
638
639 - (void)drawRect:(NSRect)rect
640 {
641     [[[VLCMain sharedInstance] mainWindow] drawFancyGradientEffectForTimeSlider];
642     msleep( 10000 ); //wait for the gradient to draw completely
643
644     /* Draw default to make sure the slider behaves correctly */
645     [[NSGraphicsContext currentContext] saveGraphicsState];
646     NSRectClip(NSZeroRect);
647     [super drawRect:rect];
648     [[NSGraphicsContext currentContext] restoreGraphicsState];
649
650     NSRect knobRect = [[self cell] knobRectFlipped:NO];
651     knobRect.origin.y+=1;
652     [self drawKnobInRect: knobRect];
653 }
654
655 @end
656
657 /*****************************************************************************
658  * ITSlider
659  *****************************************************************************/
660
661 @implementation ITSlider
662
663 - (void)awakeFromNib
664 {
665     BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
666     if (b_dark)
667         img = [NSImage imageNamed:@"volume-slider-knob_dark"];
668     else
669         img = [NSImage imageNamed:@"volume-slider-knob"];
670
671     image_rect.size = [img size];
672     image_rect.origin.x = 0;
673
674     if (b_dark)
675         image_rect.origin.y = -1;
676     else
677         image_rect.origin.y = 0;
678 }
679
680 - (void)drawKnobInRect:(NSRect)knobRect
681 {
682     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
683     knobRect.size.width = image_rect.size.width;
684     knobRect.size.height = image_rect.size.height;
685     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
686 }
687
688 - (void)drawRect:(NSRect)rect
689 {
690     /* Draw default to make sure the slider behaves correctly */
691     [[NSGraphicsContext currentContext] saveGraphicsState];
692     NSRectClip(NSZeroRect);
693     [super drawRect:rect];
694     [[NSGraphicsContext currentContext] restoreGraphicsState];
695
696     NSRect knobRect = [[self cell] knobRectFlipped:NO];
697     knobRect.origin.y+=2;
698     [self drawKnobInRect: knobRect];
699 }
700
701 @end
702
703 /*****************************************************************************
704  * VLCTimeField implementation
705  *****************************************************************************
706  * we need this to catch our click-event in the controller window
707  *****************************************************************************/
708
709 @implementation VLCTimeField
710 + (void)initialize{
711     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
712     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
713
714     [defaults registerDefaults:appDefaults];
715 }
716
717 - (void)awakeFromNib
718 {
719     NSColor *o_string_color;
720     if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
721         o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
722     else
723         o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
724
725     textAlignment = NSCenterTextAlignment;
726     o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
727 }
728
729 - (void)setAlignment:(NSTextAlignment)alignment
730 {
731     textAlignment = alignment;
732     [self setStringValue:[self stringValue]];
733 }
734
735 - (void)dealloc
736 {
737     [o_string_shadow release];
738     [o_string_attributes_dict release];
739     [super dealloc];
740 }
741
742 - (void)setStringValue:(NSString *)string
743 {
744     if (!o_string_shadow)
745     {
746         o_string_shadow = [[NSShadow alloc] init];
747         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
748         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
749         [o_string_shadow setShadowBlurRadius:0.0];
750     }
751
752     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
753     NSUInteger i_stringLength = [string length];
754
755     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
756     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
757     [self setAttributedStringValue: o_attributed_string];
758     [o_attributed_string release];
759 }
760
761 - (void)mouseDown: (NSEvent *)ourEvent
762 {
763     if( [ourEvent clickCount] > 1 )
764         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
765     else
766     {
767         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
768             [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
769         else
770             [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
771     }
772 }
773
774 - (BOOL)timeRemaining
775 {
776     return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
777 }
778 @end
779
780 /*****************************************************************************
781  * VLCMainWindowSplitView implementation
782  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
783  *****************************************************************************/
784 @implementation VLCMainWindowSplitView : NSSplitView
785 /* Return the color of the dividers that the split view is drawing between subviews. The default implementation of this method returns [NSColor clearColor] for the thick divider style. It will also return [NSColor clearColor] for the thin divider style when the split view is in a textured window. All other thin dividers are drawn with a color that looks good between two white panes. You can override this method to change the color of dividers.
786  */
787 - (NSColor *)dividerColor
788 {
789     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
790 }
791
792 /* Return the thickness of the dividers that the split view is drawing between subviews. The default implementation returns a value that depends on the divider style. You can override this method to change the size of dividers.
793  */
794 - (CGFloat)dividerThickness
795 {
796     return 1.0;
797 }
798 @end
799
800 /*****************************************************************************
801  * VLCThreePartImageView interface
802  *****************************************************************************/
803 @implementation VLCThreePartImageView
804 - (void)dealloc
805 {
806     [o_left_img release];
807     [o_middle_img release];
808     [o_right_img release];
809
810     [super dealloc];
811 }
812
813 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
814 {
815     if (o_left_img)
816         [o_left_img release];
817     if (o_middle_img)
818         [o_middle_img release];
819     if (o_right_img)
820         [o_right_img release];
821
822     o_left_img = [left retain];
823     o_middle_img = [middle retain];
824     o_right_img = [right retain];
825 }
826
827 - (void)drawRect:(NSRect)rect
828 {
829     NSRect bnds = [self bounds];
830     NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
831 }
832
833 @end
834
835 @implementation VLCThreePartDropView
836
837 - (BOOL)mouseDownCanMoveWindow
838 {
839     return YES;
840 }
841
842 - (void)dealloc
843 {
844     [self unregisterDraggedTypes];
845     [super dealloc];
846 }
847
848 - (void)awakeFromNib
849 {
850     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
851 }
852
853 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
854 {
855     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
856         return NSDragOperationGeneric;
857
858     return NSDragOperationNone;
859 }
860
861 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
862 {
863     return YES;
864 }
865
866 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
867 {
868     BOOL b_returned;
869     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
870
871     [self setNeedsDisplay:YES];
872     return YES;
873 }
874
875 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
876 {
877     [self setNeedsDisplay:YES];
878 }
879
880 @end