]> git.sesse.net Git - vlc/blob - modules/gui/macosx/fspanel.m
macosx/fspanel: add accessibility support (refs #495)
[vlc] / modules / gui / macosx / fspanel.m
1 /*****************************************************************************
2  * fspanel.m: MacOS X full screen panel
3  *****************************************************************************
4  * Copyright (C) 2006-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jérôme Decoodt <djc at videolan dot org>
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 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #import "intf.h"
29 #import "CoreInteraction.h"
30 #import "MainWindow.h"
31 #import "misc.h"
32 #import "fspanel.h"
33 #import "CompatibilityFixes.h"
34 #import <vlc_aout_intf.h>
35
36 @interface VLCFSPanel ()
37 - (void)hideMouse;
38 @end
39
40 /*****************************************************************************
41  * VLCFSPanel
42  *****************************************************************************/
43 @implementation VLCFSPanel
44 /* We override this initializer so we can set the NSBorderlessWindowMask styleMask, and set a few other important settings */
45 - (id)initWithContentRect:(NSRect)contentRect
46                 styleMask:(NSUInteger)aStyle
47                   backing:(NSBackingStoreType)bufferingType
48                     defer:(BOOL)flag
49 {
50     id win = [super initWithContentRect:contentRect styleMask:NSTexturedBackgroundWindowMask backing:bufferingType defer:flag];
51     [win setOpaque:NO];
52     [win setHasShadow: NO];
53     [win setBackgroundColor:[NSColor clearColor]];
54     if (OSX_LION)
55         [win setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
56
57     /* let the window sit on top of everything else and start out completely transparent */
58     [win setLevel:NSModalPanelWindowLevel];
59     i_device = 0;
60     hideAgainTimer = fadeTimer = nil;
61     [self setNonActive:nil];
62     return win;
63 }
64
65 - (void)awakeFromNib
66 {
67     [self setContentView:[[VLCFSPanelView alloc] initWithFrame: [self frame]]];
68     BOOL isInside = (NSPointInRect([NSEvent mouseLocation],[self frame]));
69     [[self contentView] addTrackingRect:[[self contentView] bounds] owner:self userData:nil assumeInside:isInside];
70     if (isInside)
71         [self mouseEntered:NULL];
72     if (!isInside)
73         [self mouseExited:NULL];
74
75     [self center];
76
77     /* get a notification if VLC isn't the active app anymore */
78     [[NSNotificationCenter defaultCenter]
79     addObserver: self
80        selector: @selector(setNonActive:)
81            name: NSApplicationDidResignActiveNotification
82          object: NSApp];
83
84     /* get a notification if VLC is the active app again */
85     [[NSNotificationCenter defaultCenter]
86     addObserver: self
87        selector: @selector(setActive:)
88            name: NSApplicationDidBecomeActiveNotification
89          object: NSApp];
90 }
91
92 /* make sure that we don't become key, since we can't handle hotkeys */
93 - (BOOL)canBecomeKeyWindow
94 {
95     return NO;
96 }
97
98 - (BOOL)mouseDownCanMoveWindow
99 {
100     return YES;
101 }
102
103 -(void)dealloc
104 {
105     [[NSNotificationCenter defaultCenter] removeObserver: self];
106
107     if( hideAgainTimer )
108     {
109         [hideAgainTimer invalidate];
110         [hideAgainTimer release];
111     }
112     [self setFadeTimer:nil];
113     [super dealloc];
114 }
115
116 -(void)center
117 {
118     /* centre the panel in the lower third of the screen */
119     NSPoint theCoordinate;
120     NSRect theScreensFrame;
121     NSRect theWindowsFrame;
122     NSScreen *screen;
123
124     /* user-defined screen */
125     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
126
127     if (!screen)
128     {
129         /* invalid preferences or none specified, using main screen */
130         screen = [NSScreen mainScreen];
131     }
132
133     theScreensFrame = [screen frame];
134     theWindowsFrame = [self frame];
135
136     theCoordinate.x = (theScreensFrame.size.width - theWindowsFrame.size.width) / 2 + theScreensFrame.origin.x;
137     theCoordinate.y = (theScreensFrame.size.height / 3) - theWindowsFrame.size.height + theScreensFrame.origin.y;
138     [self setFrameTopLeftPoint: theCoordinate];
139 }
140
141 - (void)setPlay
142 {
143     [[self contentView] setPlay];
144 }
145
146 - (void)setPause
147 {
148     [[self contentView] setPause];
149 }
150
151 - (void)setStreamTitle:(NSString *)o_title
152 {
153     [[self contentView] setStreamTitle: o_title];
154 }
155
156 - (void)setStreamPos:(float) f_pos andTime:(NSString *)o_time
157 {
158     [[self contentView] setStreamPos:f_pos andTime: o_time];
159 }
160
161 - (void)setSeekable:(BOOL) b_seekable
162 {
163     [[self contentView] setSeekable: b_seekable];
164 }
165
166 - (void)setVolumeLevel: (int)i_volumeLevel
167 {
168     [[self contentView] setVolumeLevel: i_volumeLevel];
169 }
170
171 - (void)setNonActive:(id)noData
172 {
173     b_nonActive = YES;
174     [self orderOut: self];
175
176     /* here's fadeOut, just without visibly fading */
177     b_displayed = NO;
178     [self setAlphaValue:0.0];
179     [self setFadeTimer:nil];
180     b_fadeQueued = NO;
181 }
182
183 - (void)setActive:(id)noData
184 {
185     b_nonActive = NO;
186     [[VLCMain sharedInstance] showFullscreenController];
187 }
188
189 /* This routine is called repeatedly to fade in the window */
190 - (void)focus:(NSTimer *)timer
191 {
192     /* we need to push ourselves to front if the vout window was closed since our last display */
193     if( b_voutWasUpdated )
194     {
195         [self orderFront: self];
196         b_voutWasUpdated = NO;
197     }
198
199     if( [self alphaValue] < 1.0 )
200         [self setAlphaValue:[self alphaValue]+0.1];
201     if( [self alphaValue] >= 1.0 )
202     {
203         b_displayed = YES;
204         [self setAlphaValue: 1.0];
205         [self setFadeTimer:nil];
206         if( b_fadeQueued )
207         {
208             b_fadeQueued=NO;
209             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unfocus:) userInfo:NULL repeats:YES]];
210         }
211     }
212 }
213
214 /* This routine is called repeatedly to hide the window */
215 - (void)unfocus:(NSTimer *)timer
216 {
217     if( b_keptVisible )
218     {
219         b_keptVisible = NO;
220         b_fadeQueued = NO;
221         [self setFadeTimer: NULL];
222         [self fadeIn];
223         return;
224     }
225     if( [self alphaValue] > 0.0 )
226         [self setAlphaValue:[self alphaValue]-0.05];
227     if( [self alphaValue] <= 0.05 )
228     {
229         b_displayed = NO;
230         [self setAlphaValue:0.0];
231         [self setFadeTimer:nil];
232         if( b_fadeQueued )
233         {
234             b_fadeQueued=NO;
235             [self setFadeTimer:
236                 [NSTimer scheduledTimerWithTimeInterval:0.1
237                                                  target:self
238                                                selector:@selector(focus:)
239                                                userInfo:NULL
240                                                 repeats:YES]];
241         }
242     }
243 }
244
245 - (void)mouseExited:(NSEvent *)theEvent
246 {
247     /* give up our focus, so the vout may show us again without letting the user clicking it */
248     vout_thread_t *p_vout = getVout();
249     if (p_vout)
250     {
251         if (var_GetBool( p_vout, "fullscreen" ))
252             [[[[VLCMainWindow sharedInstance] videoView] window] makeKeyWindow];
253         vlc_object_release( p_vout );
254     }
255 }
256
257 - (void)hideMouse
258 {
259     [NSCursor setHiddenUntilMouseMoves: YES];
260 }
261
262 - (void)fadeIn
263 {
264     /* in case that the user don't want us to appear, make sure we hide the mouse */
265
266     if( !config_GetInt( VLCIntf, "macosx-fspanel" ) )
267     {
268         float time = (float)var_CreateGetInteger( VLCIntf, "mouse-hide-timeout" ) / 1000.;
269         [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(hideMouse) userInfo:nil repeats:NO]];
270         return;
271     }
272
273     if( b_nonActive )
274         return;
275
276     [self orderFront: nil];
277
278     if( [self alphaValue] < 1.0 || b_displayed != YES )
279     {
280         if (![self fadeTimer])
281             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(focus:) userInfo:[NSNumber numberWithShort:1] repeats:YES]];
282         else if ([[[self fadeTimer] userInfo] shortValue]==0)
283             b_fadeQueued=YES;
284     }
285     [self autoHide];
286 }
287
288 - (void)fadeOut
289 {
290     if( NSPointInRect([NSEvent mouseLocation],[self frame]))
291         return;
292
293     if( ( [self alphaValue] > 0.0 ) )
294     {
295         if (![self fadeTimer])
296             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(unfocus:) userInfo:[NSNumber numberWithShort:0] repeats:YES]];
297         else if ([[[self fadeTimer] userInfo] shortValue]==1)
298             b_fadeQueued=YES;
299     }
300 }
301
302 /* triggers a timer to autoHide us again after some seconds of no activity */
303 - (void)autoHide
304 {
305     /* this will tell the timer to start over again or to start at all */
306     b_keptVisible = YES;
307
308     /* get us a valid timer */
309     if(! b_alreadyCounting )
310     {
311         i_timeToKeepVisibleInSec = var_CreateGetInteger( VLCIntf, "mouse-hide-timeout" ) / 500;
312         if( hideAgainTimer )
313         {
314             [hideAgainTimer invalidate];
315             [hideAgainTimer autorelease];
316         }
317         /* released in -autoHide and -dealloc */
318         hideAgainTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
319                                                           target: self
320                                                         selector: @selector(keepVisible:)
321                                                         userInfo: nil
322                                                          repeats: YES] retain];
323         b_alreadyCounting = YES;
324     }
325 }
326
327 - (void)keepVisible:(NSTimer *)timer
328 {
329     /* if the user triggered an action, start over again */
330     if( b_keptVisible )
331         b_keptVisible = NO;
332
333     /* count down until we hide ourselfes again and do so if necessary */
334     if( --i_timeToKeepVisibleInSec < 1 )
335     {
336         [self hideMouse];
337         [self fadeOut];
338         [hideAgainTimer invalidate]; /* released in -autoHide and -dealloc */
339         b_alreadyCounting = NO;
340     }
341 }
342
343 /* A getter and setter for our main timer that handles window fading */
344 - (NSTimer *)fadeTimer
345 {
346     return fadeTimer;
347 }
348
349 - (void)setFadeTimer:(NSTimer *)timer
350 {
351     [timer retain];
352     [fadeTimer invalidate];
353     [fadeTimer autorelease];
354     fadeTimer=timer;
355 }
356
357 - (void)mouseDown:(NSEvent *)theEvent
358 {
359     mouseClic = [theEvent locationInWindow];
360 }
361
362 - (void)mouseDragged:(NSEvent *)theEvent
363 {
364     NSPoint point = [NSEvent mouseLocation];
365     point.x -= mouseClic.x;
366     point.y -= mouseClic.y;
367     [self setFrameOrigin:point];
368 }
369
370 - (void)setVoutWasUpdated: (int)i_newdevice;
371 {
372     b_voutWasUpdated = YES;
373     if( i_newdevice != i_device )
374     {
375         i_device = i_newdevice;
376         [self center];
377     }
378 }
379 @end
380
381 /*****************************************************************************
382  * FSPanelView
383  *****************************************************************************/
384 @implementation VLCFSPanelView
385
386 #define addButton( o_button, imageOff, imageOn, _x, _y, action, AXDesc, ToolTip )               \
387     s_rc.origin.x = _x;                                                                         \
388     s_rc.origin.y = _y;                                                                         \
389     o_button = [[NSButton alloc] initWithFrame: s_rc];                                 \
390     [o_button setButtonType: NSMomentaryChangeButton];                                          \
391     [o_button setBezelStyle: NSRegularSquareBezelStyle];                                        \
392     [o_button setBordered: NO];                                                                 \
393     [o_button setFont:[NSFont systemFontOfSize:0]];                                             \
394     [o_button setImage:[NSImage imageNamed:imageOff]];                                 \
395     [o_button setAlternateImage:[NSImage imageNamed:imageOn]];                         \
396     [o_button sizeToFit];                                                                       \
397     [o_button setTarget: self];                                                                 \
398     [o_button setAction: @selector(action:)];                                                   \
399     [[o_button cell] accessibilitySetOverrideValue:AXDesc forAttribute:NSAccessibilityDescriptionAttribute]; \
400     [o_button setToolTip: ToolTip]; \
401     [self addSubview:o_button];
402
403 #define addTextfield( class, o_text, align, font, color )                                    \
404     o_text = [[class alloc] initWithFrame: s_rc];                            \
405     [o_text setDrawsBackground: NO];                                                        \
406     [o_text setBordered: NO];                                                               \
407     [o_text setEditable: NO];                                                               \
408     [o_text setSelectable: NO];                                                             \
409     [o_text setStringValue: _NS("(no item is being played)")];                                                    \
410     [o_text setAlignment: align];                                                           \
411     [o_text setTextColor: [NSColor color]];                                                 \
412     [o_text setFont:[NSFont font:[NSFont smallSystemFontSize]]];                     \
413     [self addSubview:o_text];
414
415 - (id)initWithFrame:(NSRect)frameRect
416 {
417     id view = [super initWithFrame:frameRect];
418     fillColor = [[NSColor clearColor] retain];
419     NSRect s_rc = [self frame];
420     addButton( o_prev, @"fs_skip_previous" , @"fs_skip_previous_highlight", 174, 15, prev, _NS("Click to go to the previous playlist item."), _NS("Previous") );
421     addButton( o_bwd, @"fs_rewind"        , @"fs_rewind_highlight"       , 211, 14, backward, _NS("Click and hold to skip backward through the current media."), _NS("Backward") );
422     addButton( o_play, @"fs_play"          , @"fs_play_highlight"         , 267, 10, play, _NS("Click to play or pause the current media."), _NS("Play/Pause") );
423     addButton( o_fwd, @"fs_forward"       , @"fs_forward_highlight"      , 313, 14, forward, _NS("Click and hold to skip forward through the current media."), _NS("Forward") );
424     addButton( o_next, @"fs_skip_next"     , @"fs_skip_next_highlight"    , 365, 15, next, _NS("Click to go to the next playlist item."), _NS("Next") );
425     addButton( o_fullscreen, @"fs_exit_fullscreen", @"fs_exit_fullscreen_hightlight", 507, 13, toggleFullscreen, _NS("Click to exit fullscreen playback."), _NS("Toggle Fullscreen mode") );
426 /*
427     addButton( o_button, @"image (off state)", @"image (on state)", 38, 51, something, accessibility help string, usual tool tip );
428  */
429     [o_fwd setContinuous:YES];
430     [o_bwd setContinuous:YES];
431
432     /* time slider */
433     s_rc = [self frame];
434     s_rc.origin.x = 15;
435     s_rc.origin.y = 55;
436     s_rc.size.width = 518;
437     s_rc.size.height = 9;
438     o_fs_timeSlider = [[VLCFSTimeSlider alloc] initWithFrame: s_rc];
439     [o_fs_timeSlider setMinValue:0];
440     [o_fs_timeSlider setMaxValue:10000];
441     [o_fs_timeSlider setFloatValue: 0];
442     [o_fs_timeSlider setContinuous: YES];
443     [o_fs_timeSlider setTarget: self];
444     [o_fs_volumeSlider setAction: @selector(fsTimeSliderUpdate:)];
445     [[o_fs_volumeSlider cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change current playback position.") forAttribute:NSAccessibilityDescriptionAttribute];
446     [self addSubview: o_fs_timeSlider];
447
448     /* volume slider */
449     s_rc = [self frame];
450     s_rc.origin.x = 26;
451     s_rc.origin.y = 20;
452     s_rc.size.width = 95;
453     s_rc.size.height = 10;
454     o_fs_volumeSlider = [[VLCFSVolumeSlider alloc] initWithFrame: s_rc];
455     [o_fs_volumeSlider setMinValue:0];
456     [o_fs_volumeSlider setMaxValue:AOUT_VOLUME_MAX];
457     [o_fs_volumeSlider setIntValue:AOUT_VOLUME_DEFAULT];
458     [o_fs_volumeSlider setContinuous: YES];
459     [o_fs_volumeSlider setTarget: self];
460     [o_fs_volumeSlider setAction: @selector(fsVolumeSliderUpdate:)];
461     [[o_fs_volumeSlider cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change the volume.") forAttribute:NSAccessibilityDescriptionAttribute];
462     [self addSubview: o_fs_volumeSlider];
463
464     /* time counter and stream title output fields */
465     s_rc = [self frame];
466     s_rc.origin.x = 98;
467     s_rc.origin.y = 64;
468     s_rc.size.width = 352;
469     s_rc.size.height = 14;
470     addTextfield( NSTextField, o_streamTitle_txt, NSCenterTextAlignment, systemFontOfSize, whiteColor );
471     s_rc.origin.x = 471;
472     s_rc.origin.y = 64;
473     s_rc.size.width = 65;
474     addTextfield( VLCTimeField, o_streamPosition_txt, NSRightTextAlignment, systemFontOfSize, whiteColor );
475
476     o_background_img = [[NSImage imageNamed:@"fs_background"] retain];
477     o_vol_sld_img = [[NSImage imageNamed:@"fs_volume_slider_bar"] retain];
478     o_vol_mute_img = [[NSImage imageNamed:@"fs_volume_mute"] retain];
479     o_vol_max_img = [[NSImage imageNamed:@"fs_volume_max"] retain];
480     o_time_sld_img = [[NSImage imageNamed:@"fs_time_slider"] retain];
481
482     return view;
483 }
484
485 - (void)dealloc
486 {
487     [o_background_img release];
488     [o_vol_sld_img release];
489     [o_vol_mute_img release];
490     [o_vol_max_img release];
491     [o_time_sld_img release];
492     [o_fs_timeSlider release];
493     [o_fs_volumeSlider release];
494     [o_prev release];
495     [o_next release];
496     [o_bwd release];
497     [o_play release];
498     [o_fwd release];
499     [o_fullscreen release];
500     [o_streamTitle_txt release];
501     [o_streamPosition_txt release];
502     [super dealloc];
503 }
504
505 - (void)setPlay
506 {
507     [o_play setImage:[NSImage imageNamed:@"fs_play"]];
508     [o_play setAlternateImage: [NSImage imageNamed:@"fs_play_highlight"]];
509 }
510
511 - (void)setPause
512 {
513     [o_play setImage: [NSImage imageNamed:@"fs_pause"]];
514     [o_play setAlternateImage: [NSImage imageNamed:@"fs_pause_highlight"]];
515 }
516
517 - (void)setStreamTitle:(NSString *)o_title
518 {
519     [o_streamTitle_txt setStringValue: o_title];
520 }
521
522 - (void)setStreamPos:(float) f_pos andTime:(NSString *)o_time
523 {
524     [o_streamPosition_txt setStringValue: o_time];
525     [o_fs_timeSlider setFloatValue: f_pos];
526 }
527
528 - (void)setSeekable:(BOOL)b_seekable
529 {
530     [o_bwd setEnabled: b_seekable];
531     [o_fwd setEnabled: b_seekable];
532     [o_fs_timeSlider setEnabled: b_seekable];
533 }
534
535 - (void)setVolumeLevel: (int)i_volumeLevel
536 {
537     [o_fs_volumeSlider setIntValue: i_volumeLevel];
538 }
539
540 - (IBAction)play:(id)sender
541 {
542     [[VLCCoreInteraction sharedInstance] play];
543 }
544
545 - (IBAction)forward:(id)sender
546 {
547     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
548     {
549         // we just skipped 4 "continous" events, otherwise we are too fast
550         [[VLCCoreInteraction sharedInstance] forwardExtraShort];
551         last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
552     }
553 }
554
555 - (IBAction)backward:(id)sender
556 {
557     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) > 0.16 )
558     {
559         // we just skipped 4 "continous" events, otherwise we are too fast
560         [[VLCCoreInteraction sharedInstance] backwardExtraShort];
561         last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
562     }
563 }
564
565 - (IBAction)prev:(id)sender
566 {
567     [[VLCCoreInteraction sharedInstance] previous];
568 }
569
570 - (IBAction)next:(id)sender
571 {
572     [[VLCCoreInteraction sharedInstance] next];
573 }
574
575 - (IBAction)toggleFullscreen:(id)sender
576 {
577     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
578 }
579
580 - (IBAction)fsTimeSliderUpdate:(id)sender
581 {
582     input_thread_t * p_input;
583     p_input = pl_CurrentInput( VLCIntf );
584     if( p_input != NULL )
585     {
586         vlc_value_t pos;
587
588         pos.f_float = [o_fs_timeSlider floatValue] / 10000.;
589         var_Set( p_input, "position", pos );
590         vlc_object_release( p_input );
591     }
592     [[VLCMain sharedInstance] updatePlaybackPosition];
593 }
594
595 - (IBAction)fsVolumeSliderUpdate:(id)sender
596 {
597     [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
598 }
599
600 #define addImage(image, _x, _y, mode)                                                       \
601     image_size = [image size];                                                              \
602     image_rect.size = image_size;                                                           \
603     image_rect.origin.x = 0;                                                                \
604     image_rect.origin.y = 0;                                                                \
605     frame.origin.x = _x;                                                                    \
606     frame.origin.y = _y;                                                                    \
607     frame.size = image_size;                                                                \
608     [image drawInRect:frame fromRect:image_rect operation:mode fraction:1];
609
610 - (void)drawRect:(NSRect)rect
611 {
612     NSRect frame = [self frame];
613     NSRect image_rect;
614     NSSize image_size;
615     NSImage *img;
616     addImage( o_background_img, 0, 0, NSCompositeCopy );
617     addImage( o_vol_sld_img, 26, 23, NSCompositeSourceOver );
618     addImage( o_vol_mute_img, 16, 18, NSCompositeSourceOver );
619     addImage( o_vol_max_img, 124, 18, NSCompositeSourceOver );
620     addImage( o_time_sld_img, 15, 53, NSCompositeSourceOver );
621 }
622
623 @end
624
625 /*****************************************************************************
626  * VLCFSTimeSlider
627  *****************************************************************************/
628 @implementation VLCFSTimeSlider
629 - (void)drawKnobInRect:(NSRect)knobRect
630 {
631     NSRect image_rect;
632     NSImage *img = [NSImage imageNamed:@"fs_time_slider_knob_highlight"];
633     image_rect.size = [img size];
634     image_rect.origin.x = 0;
635     image_rect.origin.y = 0;
636     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
637     knobRect.size.width = image_rect.size.width;
638     knobRect.size.height = image_rect.size.height;
639     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
640 }
641
642 - (void)drawRect:(NSRect)rect
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+=7.5;
652     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
653     [self drawKnobInRect: knobRect];
654 }
655
656 @end
657
658 /*****************************************************************************
659 * VLCFSVolumeSlider
660 *****************************************************************************/
661 @implementation VLCFSVolumeSlider
662 - (void)drawKnobInRect:(NSRect) knobRect
663 {
664     NSRect image_rect;
665     NSImage *img = [NSImage imageNamed:@"fs_volume_slider_knob"];
666     image_rect.size = [img size];
667     image_rect.origin.x = 0;
668     image_rect.origin.y = 0;
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+=6;
685     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
686     [self drawKnobInRect: knobRect];
687 }
688
689 @end
690