]> git.sesse.net Git - vlc/blob - modules/gui/macosx/misc.m
18ea7edfb560b268e1cee932937615bc19f165d4
[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             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
238     }
239 }
240
241 + (void)unblackoutScreens
242 {
243     NSUInteger blackoutWindowCount = [blackoutWindows count];
244
245     for(NSUInteger i = 0; i < blackoutWindowCount; i++)
246     {
247         VLCWindow *blackoutWindow = [blackoutWindows objectAtIndex: i];
248         [blackoutWindow closeAndAnimate: YES];
249     }
250
251     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
252 }
253
254 @end
255
256 /*****************************************************************************
257  * VLCWindow
258  *
259  *  Missing extension to NSWindow
260  *****************************************************************************/
261
262 @implementation VLCWindow
263 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
264     backing:(NSBackingStoreType)backingType defer:(BOOL)flag
265 {
266     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
267     if( self )
268     {
269         b_isset_canBecomeKeyWindow = NO;
270         /* we don't want this window to be restored on relaunch */
271         if (OSX_LION)
272             [self setRestorable:NO];
273     }
274     return self;
275 }
276
277 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
278 {
279     b_isset_canBecomeKeyWindow = YES;
280     b_canBecomeKeyWindow = canBecomeKey;
281 }
282
283 - (BOOL)canBecomeKeyWindow
284 {
285     if(b_isset_canBecomeKeyWindow)
286         return b_canBecomeKeyWindow;
287
288     return [super canBecomeKeyWindow];
289 }
290
291 - (void)setCanBecomeMainWindow: (BOOL)canBecomeMain
292 {
293     b_isset_canBecomeMainWindow = YES;
294     b_canBecomeMainWindow = canBecomeMain;
295 }
296
297 - (BOOL)canBecomeMainWindow
298 {
299     if(b_isset_canBecomeMainWindow)
300         return b_canBecomeMainWindow;
301
302     return [super canBecomeMainWindow];
303 }
304
305 - (void)closeAndAnimate: (BOOL)animate
306 {
307     NSInvocation *invoc;
308
309     if (!animate)
310     {
311         [super close];
312         return;
313     }
314
315     invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(close)]];
316     [invoc setTarget: self];
317
318     if (![self isVisible] || [self alphaValue] == 0.0)
319     {
320         [super close];
321         return;
322     }
323
324     [self orderOut: self animate: YES callback: invoc];
325 }
326
327 - (void)orderOut: (id)sender animate: (BOOL)animate
328 {
329     NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[super methodSignatureForSelector:@selector(orderOut:)]];
330     [invoc setTarget: self];
331     [invoc setArgument: sender atIndex: 0];
332     [self orderOut: sender animate: animate callback: invoc];
333 }
334
335 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
336 {
337     NSViewAnimation *anim;
338     NSViewAnimation *current_anim;
339     NSMutableDictionary *dict;
340
341     if (!animate)
342     {
343         [self orderOut: sender];
344         return;
345     }
346
347     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
348
349     [dict setObject:self forKey:NSViewAnimationTargetKey];
350
351     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
352     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
353     [dict release];
354
355     [anim setAnimationBlockingMode:NSAnimationNonblocking];
356     [anim setDuration:0.9];
357     [anim setFrameRate:30];
358     [anim setUserInfo: callback];
359
360     @synchronized(self) {
361         current_anim = self->animation;
362
363         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating])
364         {
365             [anim release];
366         }
367         else
368         {
369             if (current_anim)
370             {
371                 [current_anim stopAnimation];
372                 [anim setCurrentProgress:1.0-[current_anim currentProgress]];
373                 [current_anim release];
374             }
375             else
376                 [anim setCurrentProgress:1.0 - [self alphaValue]];
377             self->animation = anim;
378             [self setDelegate: self];
379             [anim startAnimation];
380         }
381     }
382 }
383
384 - (void)orderFront: (id)sender animate: (BOOL)animate
385 {
386     NSViewAnimation *anim;
387     NSViewAnimation *current_anim;
388     NSMutableDictionary *dict;
389
390     if (!animate)
391     {
392         [super orderFront: sender];
393         [self setAlphaValue: 1.0];
394         return;
395     }
396
397     if (![self isVisible])
398     {
399         [self setAlphaValue: 0.0];
400         [super orderFront: sender];
401     }
402     else if ([self alphaValue] == 1.0)
403     {
404         [super orderFront: self];
405         return;
406     }
407
408     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
409
410     [dict setObject:self forKey:NSViewAnimationTargetKey];
411
412     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
413     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
414     [dict release];
415
416     [anim setAnimationBlockingMode:NSAnimationNonblocking];
417     [anim setDuration:0.5];
418     [anim setFrameRate:30];
419
420     @synchronized(self) {
421         current_anim = self->animation;
422
423         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating])
424         {
425             [anim release];
426         }
427         else
428         {
429             if (current_anim)
430             {
431                 [current_anim stopAnimation];
432                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
433                 [current_anim release];
434             }
435             else
436                 [anim setCurrentProgress:[self alphaValue]];
437             self->animation = anim;
438             [self setDelegate: self];
439             [self orderFront: sender];
440             [anim startAnimation];
441         }
442     }
443 }
444
445 - (void)animationDidEnd:(NSAnimation*)anim
446 {
447     if ([self alphaValue] <= 0.0)
448     {
449         NSInvocation * invoc;
450         [super orderOut: nil];
451         [self setAlphaValue: 1.0];
452         if ((invoc = [anim userInfo]))
453             [invoc invoke];
454     }
455 }
456
457 - (IBAction)fullscreen:(id)sender
458 {
459     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
460 }
461
462 @end
463
464 /*****************************************************************************
465  * VLBrushedMetalImageView
466  *****************************************************************************/
467
468 @implementation VLBrushedMetalImageView
469
470 - (BOOL)mouseDownCanMoveWindow
471 {
472     return YES;
473 }
474
475 - (void)dealloc
476 {
477     [self unregisterDraggedTypes];
478     [super dealloc];
479 }
480
481 - (void)awakeFromNib
482 {
483     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
484     [self setImageScaling: NSScaleToFit];
485     [self setImageFrameStyle: NSImageFrameNone];
486     [self setImageAlignment: NSImageAlignCenter];
487 }
488
489 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
490 {
491     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
492         return NSDragOperationGeneric;
493
494     return NSDragOperationNone;
495 }
496
497 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
498 {
499     return YES;
500 }
501
502 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
503 {
504     BOOL b_returned;
505     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
506
507     [self setNeedsDisplay:YES];
508     return b_returned;
509 }
510
511 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
512 {
513     [self setNeedsDisplay:YES];
514 }
515
516 @end
517
518
519 /*****************************************************************************
520  * MPSlider
521  *****************************************************************************/
522 @implementation MPSlider
523
524 void _drawKnobInRect(NSRect knobRect)
525 {
526     // Center knob in given rect
527     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
528     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
529
530     // Draw diamond
531     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
532     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
533     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
534     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
535     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
536     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
537     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
538 }
539
540 void _drawFrameInRect(NSRect frameRect)
541 {
542     // Draw frame
543     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
544     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
545     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
546     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
547 }
548
549 - (void)drawRect:(NSRect)rect
550 {
551     // Draw default to make sure the slider behaves correctly
552     [[NSGraphicsContext currentContext] saveGraphicsState];
553     NSRectClip(NSZeroRect);
554     [super drawRect:rect];
555     [[NSGraphicsContext currentContext] restoreGraphicsState];
556
557     // Full size
558     rect = [self bounds];
559     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
560     rect.origin.x += diff-1;
561     rect.origin.y += diff;
562     rect.size.width -= 2*diff-2;
563     rect.size.height -= 2*diff;
564
565     // Draw dark
566     NSRect knobRect = [[self cell] knobRectFlipped:NO];
567     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
568     _drawFrameInRect(rect);
569     _drawKnobInRect(knobRect);
570
571     // Draw shadow
572     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
573     rect.origin.x++;
574     rect.origin.y++;
575     knobRect.origin.x++;
576     knobRect.origin.y++;
577     _drawFrameInRect(rect);
578     _drawKnobInRect(knobRect);
579 }
580
581 @end
582
583 /*****************************************************************************
584  * TimeLineSlider
585  *****************************************************************************/
586
587 @implementation TimeLineSlider
588
589 - (void)awakeFromNib
590 {
591     if (config_GetInt( VLCIntf, "macosx-interfacestyle" )) {
592         o_knob_img = [NSImage imageNamed:@"progression-knob_dark"];
593         b_dark = YES;
594     } else {
595         o_knob_img = [NSImage imageNamed:@"progression-knob"];
596         b_dark = NO;
597     }
598     img_rect.size = [o_knob_img size];
599     img_rect.origin.x = img_rect.origin.y = 0;
600 }
601
602 - (void)dealloc
603 {
604     [o_knob_img release];
605     [super dealloc];
606 }
607
608 - (CGFloat)knobPosition
609 {
610     NSRect knobRect = [[self cell] knobRectFlipped:NO];
611     knobRect.origin.x += knobRect.size.width / 2;
612     return knobRect.origin.x;
613 }
614
615 - (void)drawKnobInRect:(NSRect)knobRect
616 {
617     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
618     knobRect.size.width = img_rect.size.width;
619     knobRect.size.height = img_rect.size.height;
620     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
621 }
622
623 - (void)drawRect:(NSRect)rect
624 {
625     [[[VLCMain sharedInstance] mainWindow] drawFancyGradientEffectForTimeSlider];
626     msleep( 10000 ); //wait for the gradient to draw completely
627
628     /* Draw default to make sure the slider behaves correctly */
629     [[NSGraphicsContext currentContext] saveGraphicsState];
630     NSRectClip(NSZeroRect);
631     [super drawRect:rect];
632     [[NSGraphicsContext currentContext] restoreGraphicsState];
633
634     NSRect knobRect = [[self cell] knobRectFlipped:NO];
635     if (b_dark)
636         knobRect.origin.y+=2;
637     else
638         knobRect.origin.y+=1;
639     [self drawKnobInRect: knobRect];
640 }
641
642 @end
643
644 /*****************************************************************************
645  * ITSlider
646  *****************************************************************************/
647
648 @implementation ITSlider
649
650 - (void)awakeFromNib
651 {
652     BOOL b_dark = config_GetInt( VLCIntf, "macosx-interfacestyle" );
653     if (b_dark)
654         img = [NSImage imageNamed:@"volume-slider-knob_dark"];
655     else
656         img = [NSImage imageNamed:@"volume-slider-knob"];
657
658     image_rect.size = [img size];
659     image_rect.origin.x = 0;
660
661     if (b_dark)
662         image_rect.origin.y = -1;
663     else
664         image_rect.origin.y = 0;
665 }
666
667 - (void)drawKnobInRect:(NSRect)knobRect
668 {
669     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
670     knobRect.size.width = image_rect.size.width;
671     knobRect.size.height = image_rect.size.height;
672     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
673 }
674
675 - (void)drawRect:(NSRect)rect
676 {
677     /* Draw default to make sure the slider behaves correctly */
678     [[NSGraphicsContext currentContext] saveGraphicsState];
679     NSRectClip(NSZeroRect);
680     [super drawRect:rect];
681     [[NSGraphicsContext currentContext] restoreGraphicsState];
682
683     NSRect knobRect = [[self cell] knobRectFlipped:NO];
684     knobRect.origin.y+=2;
685     [self drawKnobInRect: knobRect];
686 }
687
688 @end
689
690 /*****************************************************************************
691  * VLCTimeField implementation
692  *****************************************************************************
693  * we need this to catch our click-event in the controller window
694  *****************************************************************************/
695
696 @implementation VLCTimeField
697 + (void)initialize{
698     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
699     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
700
701     [defaults registerDefaults:appDefaults];
702 }
703
704 - (void)awakeFromNib
705 {
706     NSColor *o_string_color;
707     if (!config_GetInt( VLCIntf, "macosx-interfacestyle"))
708         o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
709     else
710         o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
711
712     textAlignment = NSCenterTextAlignment;
713     o_string_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: o_string_color, NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:10.0], NSFontAttributeName, nil] retain];
714 }
715
716 - (void)setAlignment:(NSTextAlignment)alignment
717 {
718     textAlignment = alignment;
719     [self setStringValue:[self stringValue]];
720 }
721
722 - (void)dealloc
723 {
724     [o_string_shadow release];
725     [o_string_attributes_dict release];
726     [super dealloc];
727 }
728
729 - (void)setStringValue:(NSString *)string
730 {
731     if (!o_string_shadow)
732     {
733         o_string_shadow = [[NSShadow alloc] init];
734         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
735         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
736         [o_string_shadow setShadowBlurRadius:0.0];
737     }
738
739     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: o_string_attributes_dict];
740     NSUInteger i_stringLength = [string length];
741
742     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
743     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
744     [self setAttributedStringValue: o_attributed_string];
745     [o_attributed_string release];
746 }
747
748 - (void)mouseDown: (NSEvent *)ourEvent
749 {
750     if( [ourEvent clickCount] > 1 )
751         [[[VLCMain sharedInstance] controls] goToSpecificTime: nil];
752     else
753     {
754         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"])
755             [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"DisplayTimeAsTimeRemaining"];
756         else
757             [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"DisplayTimeAsTimeRemaining"];
758     }
759 }
760
761 - (BOOL)timeRemaining
762 {
763     return [[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayTimeAsTimeRemaining"];
764 }
765 @end
766
767 /*****************************************************************************
768  * VLCMainWindowSplitView implementation
769  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
770  *****************************************************************************/
771 @implementation VLCMainWindowSplitView : NSSplitView
772 /* 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.
773  */
774 - (NSColor *)dividerColor
775 {
776     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
777 }
778
779 /* 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.
780  */
781 - (CGFloat)dividerThickness
782 {
783     return 1.0;
784 }
785 @end
786
787 /*****************************************************************************
788  * VLCThreePartImageView interface
789  *****************************************************************************/
790 @implementation VLCThreePartImageView
791 - (void)dealloc
792 {
793     [o_left_img release];
794     [o_middle_img release];
795     [o_right_img release];
796
797     [super dealloc];
798 }
799
800 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
801 {
802     if (o_left_img)
803         [o_left_img release];
804     if (o_middle_img)
805         [o_middle_img release];
806     if (o_right_img)
807         [o_right_img release];
808
809     o_left_img = [left retain];
810     o_middle_img = [middle retain];
811     o_right_img = [right retain];
812 }
813
814 - (void)drawRect:(NSRect)rect
815 {
816     NSRect bnds = [self bounds];
817     NSDrawThreePartImage( bnds, o_left_img, o_middle_img, o_right_img, NO, NSCompositeSourceOver, 1, NO );
818 }
819
820 @end
821
822 @implementation VLCThreePartDropView
823
824 - (BOOL)mouseDownCanMoveWindow
825 {
826     return YES;
827 }
828
829 - (void)dealloc
830 {
831     [self unregisterDraggedTypes];
832     [super dealloc];
833 }
834
835 - (void)awakeFromNib
836 {
837     [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
838 }
839
840 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
841 {
842     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
843         return NSDragOperationGeneric;
844
845     return NSDragOperationNone;
846 }
847
848 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
849 {
850     return YES;
851 }
852
853 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
854 {
855     BOOL b_returned;
856     b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
857
858     [self setNeedsDisplay:YES];
859     return YES;
860 }
861
862 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
863 {
864     [self setNeedsDisplay:YES];
865 }
866
867 @end