]> git.sesse.net Git - vlc/blob - modules/gui/macosx/fspanel.m
macosx: fix visual appearance of fullscreen tic (refs #8628)
[vlc] / modules / gui / macosx / fspanel.m
1 /*****************************************************************************
2  * fspanel.m: MacOS X full screen panel
3  *****************************************************************************
4  * Copyright (C) 2006-2013 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  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #import "intf.h"
30 #import "CoreInteraction.h"
31 #import "MainWindow.h"
32 #import "misc.h"
33 #import "fspanel.h"
34 #import "CompatibilityFixes.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_SNOW_LEOPARD)
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 = config_GetInt(VLCIntf, "macosx-vdev");
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     if (!OSX_SNOW_LEOPARD)
76         [self setAnimationBehavior:NSWindowAnimationBehaviorNone];
77
78     /* get a notification if VLC isn't the active app anymore */
79     [[NSNotificationCenter defaultCenter]
80     addObserver: self
81        selector: @selector(setNonActive:)
82            name: NSApplicationDidResignActiveNotification
83          object: NSApp];
84
85     /* Get a notification if VLC is the active app again.
86      Needed as becomeKeyWindow does not get called when window is activated by clicking */
87     [[NSNotificationCenter defaultCenter]
88     addObserver: self
89        selector: @selector(setActive:)
90            name: NSApplicationDidBecomeActiveNotification
91          object: NSApp];
92 }
93
94 /* make sure that we don't become key, since we can't handle hotkeys */
95 - (BOOL)canBecomeKeyWindow
96 {
97     return NO;
98 }
99
100 - (BOOL)mouseDownCanMoveWindow
101 {
102     return YES;
103 }
104
105 -(void)dealloc
106 {
107     [[NSNotificationCenter defaultCenter] removeObserver: self];
108
109     if (hideAgainTimer) {
110         [hideAgainTimer invalidate];
111         [hideAgainTimer release];
112     }
113
114     if (o_vout_window)
115         [o_vout_window release];
116
117     [self setFadeTimer:nil];
118     [super dealloc];
119 }
120
121 -(void)center
122 {
123     /* centre the panel in the lower third of the screen */
124     NSPoint theCoordinate;
125     NSRect theScreensFrame;
126     NSRect theWindowsFrame;
127     NSScreen *screen;
128
129     /* user-defined screen */
130     screen = [NSScreen screenWithDisplayID: (CGDirectDisplayID)i_device];
131
132     if (!screen)
133         /* invalid preferences or none specified, using main screen */
134         screen = [NSScreen mainScreen];
135
136     theScreensFrame = [screen frame];
137     theWindowsFrame = [self frame];
138
139     theCoordinate.x = (theScreensFrame.size.width - theWindowsFrame.size.width) / 2 + theScreensFrame.origin.x;
140     theCoordinate.y = (theScreensFrame.size.height / 3) - theWindowsFrame.size.height + theScreensFrame.origin.y;
141     [self setFrameTopLeftPoint: theCoordinate];
142 }
143
144 - (void)setPlay
145 {
146     [[self contentView] setPlay];
147 }
148
149 - (void)setPause
150 {
151     [[self contentView] setPause];
152 }
153
154 - (void)setStreamTitle:(NSString *)o_title
155 {
156     [[self contentView] setStreamTitle: o_title];
157 }
158
159 - (void)updatePositionAndTime
160 {
161     [[self contentView] updatePositionAndTime];
162 }
163
164 - (void)setSeekable:(BOOL) b_seekable
165 {
166     [[self contentView] setSeekable: b_seekable];
167 }
168
169 - (void)setVolumeLevel: (int)i_volumeLevel
170 {
171     [[self contentView] setVolumeLevel: i_volumeLevel];
172 }
173
174 - (void)setNonActive:(id)noData
175 {
176     b_nonActive = YES;
177
178     /* here's fadeOut, just without visibly fading */
179     b_displayed = NO;
180     [self setAlphaValue:0.0];
181     [self setFadeTimer:nil];
182
183     b_fadeQueued = NO;
184
185     [self orderOut: self];
186 }
187
188 - (void)setActive:(id)noData
189 {
190     b_nonActive = NO;
191
192     [[VLCMain sharedInstance] showFullscreenController];
193 }
194
195 /* This routine is called repeatedly to fade in the window */
196 - (void)focus:(NSTimer *)timer
197 {
198     /* we need to push ourselves to front if the vout window was closed since our last display */
199     if (b_voutWasUpdated) {
200         [self orderFront: self];
201         b_voutWasUpdated = NO;
202     }
203
204     if ([self alphaValue] < 1.0) {
205         [self setAlphaValue:[self alphaValue]+0.1];
206     }
207     if ([self alphaValue] >= 1.0) {
208         b_displayed = YES;
209         [self setAlphaValue: 1.0];
210         [self setFadeTimer:nil];
211         if (b_fadeQueued) {
212             b_fadeQueued=NO;
213             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unfocus:) userInfo:NULL repeats:YES]];
214         }
215     }
216 }
217
218 /* This routine is called repeatedly to hide the window */
219 - (void)unfocus:(NSTimer *)timer
220 {
221     if (b_keptVisible) {
222         b_keptVisible = NO;
223         b_fadeQueued = NO;
224         [self setFadeTimer: NULL];
225         [self fadeIn];
226         return;
227     }
228     if ([self alphaValue] > 0.0) {
229         [self setAlphaValue:[self alphaValue]-0.05];
230     }
231     if ([self alphaValue] <= 0.05) {
232         b_displayed = NO;
233         [self setAlphaValue:0.0];
234         [self setFadeTimer:nil];
235         if (b_fadeQueued) {
236             b_fadeQueued=NO;
237             [self setFadeTimer:
238                 [NSTimer scheduledTimerWithTimeInterval:0.1
239                                                  target:self
240                                                selector:@selector(focus:)
241                                                userInfo:NULL
242                                                 repeats:YES]];
243         }
244     }
245 }
246
247 - (void)mouseExited:(NSEvent *)theEvent
248 {
249     /* give up our focus, so the vout may show us again without letting the user clicking it */
250     if (o_vout_window && var_GetBool(pl_Get(VLCIntf), "fullscreen"))
251         [o_vout_window makeKeyWindow];
252 }
253
254 - (void)hideMouse
255 {
256     [NSCursor setHiddenUntilMouseMoves: YES];
257 }
258
259 - (void)fadeIn
260 {
261     /* in case that the user don't want us to appear, make sure we hide the mouse */
262
263     if (!config_GetInt(VLCIntf, "macosx-fspanel")) {
264         float time = (float)var_CreateGetInteger(VLCIntf, "mouse-hide-timeout") / 1000.;
265         [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(hideMouse) userInfo:nil repeats:NO]];
266         return;
267     }
268
269     if (b_nonActive)
270         return;
271
272     [self orderFront: nil];
273
274     if ([self alphaValue] < 1.0 || b_displayed != YES) {
275         if (![self fadeTimer])
276             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(focus:) userInfo:@1 repeats:YES]];
277         else if ([[[self fadeTimer] userInfo] shortValue]==0)
278             b_fadeQueued=YES;
279     }
280     [self autoHide];
281 }
282
283 - (void)fadeOut
284 {
285     if (NSPointInRect([NSEvent mouseLocation],[self frame]))
286         return;
287
288     if (([self alphaValue] > 0.0)) {
289         if (![self fadeTimer])
290             [self setFadeTimer:[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(unfocus:) userInfo:@0 repeats:YES]];
291         else if ([[[self fadeTimer] userInfo] shortValue]==1)
292             b_fadeQueued=YES;
293     }
294 }
295
296 /* triggers a timer to autoHide us again after some seconds of no activity */
297 - (void)autoHide
298 {
299     /* this will tell the timer to start over again or to start at all */
300     b_keptVisible = YES;
301
302     /* get us a valid timer */
303     if (!b_alreadyCounting) {
304         i_timeToKeepVisibleInSec = var_CreateGetInteger(VLCIntf, "mouse-hide-timeout") / 500;
305         if (hideAgainTimer) {
306             [hideAgainTimer invalidate];
307             [hideAgainTimer autorelease];
308         }
309         /* released in -autoHide and -dealloc */
310         hideAgainTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
311                                                           target: self
312                                                         selector: @selector(keepVisible:)
313                                                         userInfo: nil
314                                                          repeats: YES] retain];
315         b_alreadyCounting = YES;
316     }
317 }
318
319 - (void)keepVisible:(NSTimer *)timer
320 {
321     /* if the user triggered an action, start over again */
322     if (b_keptVisible)
323         b_keptVisible = NO;
324
325     /* count down until we hide ourselfes again and do so if necessary */
326     if (--i_timeToKeepVisibleInSec < 1) {
327         [self hideMouse];
328         [self fadeOut];
329         [hideAgainTimer invalidate]; /* released in -autoHide and -dealloc */
330         b_alreadyCounting = NO;
331     }
332 }
333
334 /* A getter and setter for our main timer that handles window fading */
335 - (NSTimer *)fadeTimer
336 {
337     return fadeTimer;
338 }
339
340 - (void)setFadeTimer:(NSTimer *)timer
341 {
342     [timer retain];
343     [fadeTimer invalidate];
344     [fadeTimer autorelease];
345     fadeTimer=timer;
346 }
347
348 - (void)mouseDown:(NSEvent *)theEvent
349 {
350     mouseClic = [theEvent locationInWindow];
351 }
352
353 - (void)mouseDragged:(NSEvent *)theEvent
354 {
355     NSPoint point = [NSEvent mouseLocation];
356     point.x -= mouseClic.x;
357     point.y -= mouseClic.y;
358     [self setFrameOrigin:point];
359 }
360
361 - (void)setVoutWasUpdated: (VLCWindow *)o_window
362 {
363     b_voutWasUpdated = YES;
364     if (o_vout_window)
365         [o_vout_window release];
366     o_vout_window = [o_window retain];
367     int i_newdevice = (int)[[o_vout_window screen] displayID];
368     if ((i_newdevice != i_device && i_device != 0) || i_newdevice != [[self screen] displayID]) {
369         i_device = i_newdevice;
370         [self center];
371     } else
372         i_device = i_newdevice;
373 }
374 @end
375
376 /*****************************************************************************
377  * FSPanelView
378  *****************************************************************************/
379 @implementation VLCFSPanelView
380
381 #define addButton(o_button, imageOff, imageOn, _x, _y, action, AXDesc, ToolTip)               \
382     s_rc.origin.x = _x;                                                                         \
383     s_rc.origin.y = _y;                                                                         \
384     o_button = [[NSButton alloc] initWithFrame: s_rc];                                 \
385     [o_button setButtonType: NSMomentaryChangeButton];                                          \
386     [o_button setBezelStyle: NSRegularSquareBezelStyle];                                        \
387     [o_button setBordered: NO];                                                                 \
388     [o_button setFont:[NSFont systemFontOfSize:0]];                                             \
389     [o_button setImage:[NSImage imageNamed:imageOff]];                                 \
390     [o_button setAlternateImage:[NSImage imageNamed:imageOn]];                         \
391     [o_button sizeToFit];                                                                       \
392     [o_button setTarget: self];                                                                 \
393     [o_button setAction: @selector(action:)];                                                   \
394     [[o_button cell] accessibilitySetOverrideValue:AXDesc forAttribute:NSAccessibilityDescriptionAttribute]; \
395     [[o_button cell] accessibilitySetOverrideValue:ToolTip forAttribute:NSAccessibilityTitleAttribute]; \
396     [o_button setToolTip: ToolTip]; \
397     [self addSubview:o_button];
398
399 #define addTextfield(class, o_text, align, font, color)                                    \
400     o_text = [[class alloc] initWithFrame: s_rc];                            \
401     [o_text setDrawsBackground: NO];                                                        \
402     [o_text setBordered: NO];                                                               \
403     [o_text setEditable: NO];                                                               \
404     [o_text setSelectable: NO];                                                             \
405     [o_text setStringValue: _NS("(no item is being played)")];                                                    \
406     [o_text setAlignment: align];                                                           \
407     [o_text setTextColor: [NSColor color]];                                                 \
408     [o_text setFont:[NSFont font:[NSFont smallSystemFontSize]]];                     \
409     [self addSubview:o_text];
410
411 - (id)initWithFrame:(NSRect)frameRect
412 {
413     id view = [super initWithFrame:frameRect];
414     fillColor = [[NSColor clearColor] retain];
415     NSRect s_rc = [self frame];
416     addButton(o_prev, @"fs_skip_previous_highlight" , @"fs_skip_previous", 174, 15, prev, _NS("Click to go to the previous playlist item."), _NS("Previous"));
417     addButton(o_bwd, @"fs_rewind_highlight"        , @"fs_rewind"       , 211, 14, backward, _NS("Click and hold to skip backward through the current media."), _NS("Backward"));
418     addButton(o_play, @"fs_play_highlight"          , @"fs_play"         , 265, 10, play, _NS("Click to play or pause the current media."), _NS("Play/Pause"));
419     addButton(o_fwd, @"fs_forward_highlight"       , @"fs_forward"      , 313, 14, forward, _NS("Click and hold to skip forward through the current media."), _NS("Forward"));
420     addButton(o_next, @"fs_skip_next_highlight"     , @"fs_skip_next"    , 365, 15, next, _NS("Click to go to the next playlist item."), _NS("Next"));
421     addButton(o_fullscreen, @"fs_exit_fullscreen_highlight", @"fs_exit_fullscreen", 507, 13, toggleFullscreen, _NS("Click to exit fullscreen playback."), _NS("Toggle Fullscreen mode"));
422 /*
423     addButton(o_button, @"image (off state)", @"image (on state)", 38, 51, something, accessibility help string, usual tool tip);
424  */
425     [o_fwd setContinuous:YES];
426     [o_bwd setContinuous:YES];
427
428     /* time slider */
429     // (surrounding progress view for swipe behaviour)
430     s_rc.origin.x = 15;
431     s_rc.origin.y = 45;
432     s_rc.size.width = 518;
433     s_rc.size.height = 13;
434     o_progress_view = [[VLCProgressView alloc] initWithFrame: s_rc];
435     s_rc.origin.x = 0;
436     s_rc.origin.y = 0;
437     o_fs_timeSlider = [[VLCFSTimeSlider alloc] initWithFrame: s_rc];
438     [o_fs_timeSlider setMinValue:0];
439     [o_fs_timeSlider setMaxValue:10000];
440     [o_fs_timeSlider setFloatValue: 0];
441     [o_fs_timeSlider setContinuous: YES];
442     [o_fs_timeSlider setTarget: self];
443     [o_fs_timeSlider setAction: @selector(fsTimeSliderUpdate:)];
444     [[o_fs_volumeSlider cell] accessibilitySetOverrideValue:_NS("Position") forAttribute:NSAccessibilityTitleAttribute];
445     [[o_fs_timeSlider 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_progress_view];
447     [o_progress_view addSubview: o_fs_timeSlider];
448
449     /* volume slider */
450     s_rc = [self frame];
451     s_rc.origin.x = 26;
452     s_rc.origin.y = 20;
453     s_rc.size.width = 95;
454     s_rc.size.height = 12;
455     o_fs_volumeSlider = [[VLCFSVolumeSlider alloc] initWithFrame: s_rc];
456     [o_fs_volumeSlider setMinValue:0];
457     [o_fs_volumeSlider setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]];
458     [o_fs_volumeSlider setIntValue:AOUT_VOLUME_DEFAULT];
459     [o_fs_volumeSlider setContinuous: YES];
460     [o_fs_volumeSlider setTarget: self];
461     [o_fs_volumeSlider setAction: @selector(fsVolumeSliderUpdate:)];
462     [o_fs_volumeSlider setUsesBrightArtwork:NO];
463     [[o_fs_volumeSlider cell] accessibilitySetOverrideValue:_NS("Volume") forAttribute:NSAccessibilityTitleAttribute];
464     [[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];
465     [self addSubview: o_fs_volumeSlider];
466
467     /* time counter and stream title output fields */
468     s_rc = [self frame];
469     // 10 px gap between time fields
470     s_rc.origin.x = 90;
471     s_rc.origin.y = 64;
472     s_rc.size.width = 361;
473     s_rc.size.height = 14;
474     addTextfield(NSTextField, o_streamTitle_txt, NSCenterTextAlignment, systemFontOfSize, whiteColor);
475     s_rc.origin.x = 15;
476     s_rc.origin.y = 64;
477     s_rc.size.width = 65;
478     addTextfield(VLCTimeField, o_streamPosition_txt, NSLeftTextAlignment, systemFontOfSize, whiteColor);
479     s_rc.origin.x = 471;
480     s_rc.origin.y = 64;
481     s_rc.size.width = 65;
482     addTextfield(VLCTimeField, o_streamLength_txt, NSRightTextAlignment, systemFontOfSize, whiteColor);
483     [o_streamLength_txt setRemainingIdentifier: @"DisplayFullscreenTimeAsTimeRemaining"];
484
485     o_background_img = [[NSImage imageNamed:@"fs_background"] retain];
486     o_vol_sld_img = [[NSImage imageNamed:@"fs_volume_slider_bar"] retain];
487     o_vol_mute_img = [[NSImage imageNamed:@"fs_volume_mute_highlight"] retain];
488     o_vol_max_img = [[NSImage imageNamed:@"fs_volume_max_highlight"] retain];
489     o_time_sld_img = [[NSImage imageNamed:@"fs_time_slider"] retain];
490
491     return view;
492 }
493
494 - (void)dealloc
495 {
496     [o_background_img release];
497     [o_vol_sld_img release];
498     [o_vol_mute_img release];
499     [o_vol_max_img release];
500     [o_time_sld_img release];
501     [o_fs_timeSlider release];
502     [o_fs_volumeSlider release];
503     [o_prev release];
504     [o_next release];
505     [o_bwd release];
506     [o_play release];
507     [o_fwd release];
508     [o_fullscreen release];
509     [o_streamTitle_txt release];
510     [o_streamPosition_txt release];
511     [super dealloc];
512 }
513
514 - (void)setPlay
515 {
516     [o_play setImage:[NSImage imageNamed:@"fs_play_highlight"]];
517     [o_play setAlternateImage: [NSImage imageNamed:@"fs_play"]];
518 }
519
520 - (void)setPause
521 {
522     [o_play setImage: [NSImage imageNamed:@"fs_pause_highlight"]];
523     [o_play setAlternateImage: [NSImage imageNamed:@"fs_pause"]];
524 }
525
526 - (void)setStreamTitle:(NSString *)o_title
527 {
528     [o_streamTitle_txt setStringValue: o_title];
529 }
530
531 - (void)updatePositionAndTime
532 {
533     input_thread_t * p_input;
534     p_input = pl_CurrentInput(VLCIntf);
535     if (p_input) {
536         
537         vlc_value_t pos;
538         float f_updated;
539
540         var_Get(p_input, "position", &pos);
541         f_updated = 10000. * pos.f_float;
542         [o_fs_timeSlider setFloatValue: f_updated];
543
544         vlc_value_t time;
545         char psz_time[MSTRTIME_MAX_SIZE];
546
547         var_Get(p_input, "time", &time);
548         mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
549
550         // update total duration (right field)
551         if(dur <= 0) {
552             [o_streamLength_txt setHidden: YES];
553         } else {
554             [o_streamLength_txt setHidden: NO];
555
556             NSString *o_total_time;
557             if ([o_streamLength_txt timeRemaining]) {
558                 mtime_t remaining = 0;
559                 if (dur > time.i_time)
560                     remaining = dur - time.i_time;
561                 o_total_time = [NSString stringWithFormat: @"-%s", secstotimestr(psz_time, (remaining / 1000000))];
562             } else
563                 o_total_time = @(secstotimestr(psz_time, (dur / 1000000)));
564
565             [o_streamLength_txt setStringValue: o_total_time];
566         }
567
568         // update current position (left field)
569         NSString *o_playback_pos = @(secstotimestr(psz_time, (time.i_time / 1000000)));
570                
571         [o_streamPosition_txt setStringValue: o_playback_pos];
572         vlc_object_release(p_input);
573     } else {
574         [o_fs_timeSlider setFloatValue: 0.0];
575         [o_streamPosition_txt setStringValue: @"00:00"];
576         [o_streamLength_txt setHidden: YES];
577     }
578
579 }
580
581 - (void)setSeekable:(BOOL)b_seekable
582 {
583     [o_bwd setEnabled: b_seekable];
584     [o_fwd setEnabled: b_seekable];
585     [o_fs_timeSlider setEnabled: b_seekable];
586 }
587
588 - (void)setVolumeLevel: (int)i_volumeLevel
589 {
590     [o_fs_volumeSlider setIntValue: i_volumeLevel];
591 }
592
593 - (IBAction)play:(id)sender
594 {
595     [[VLCCoreInteraction sharedInstance] playOrPause];
596 }
597
598 - (IBAction)forward:(id)sender
599 {
600     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
601         // we just skipped 4 "continous" events, otherwise we are too fast
602         [[VLCCoreInteraction sharedInstance] forwardExtraShort];
603         last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
604     }
605 }
606
607 - (IBAction)backward:(id)sender
608 {
609     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) > 0.16) {
610         // we just skipped 4 "continous" events, otherwise we are too fast
611         [[VLCCoreInteraction sharedInstance] backwardExtraShort];
612         last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
613     }
614 }
615
616 - (IBAction)prev:(id)sender
617 {
618     [[VLCCoreInteraction sharedInstance] previous];
619 }
620
621 - (IBAction)next:(id)sender
622 {
623     [[VLCCoreInteraction sharedInstance] next];
624 }
625
626 - (IBAction)toggleFullscreen:(id)sender
627 {
628     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
629 }
630
631 - (IBAction)fsTimeSliderUpdate:(id)sender
632 {
633     input_thread_t * p_input;
634     p_input = pl_CurrentInput(VLCIntf);
635     if (p_input != NULL) {
636         vlc_value_t pos;
637
638         pos.f_float = [o_fs_timeSlider floatValue] / 10000.;
639         var_Set(p_input, "position", pos);
640         vlc_object_release(p_input);
641     }
642     [[VLCMain sharedInstance] updatePlaybackPosition];
643 }
644
645 - (IBAction)fsVolumeSliderUpdate:(id)sender
646 {
647     [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
648 }
649
650 #define addImage(image, _x, _y, mode)                                                       \
651     image_size = [image size];                                                              \
652     image_rect.size = image_size;                                                           \
653     image_rect.origin.x = 0;                                                                \
654     image_rect.origin.y = 0;                                                                \
655     frame.origin.x = _x;                                                                    \
656     frame.origin.y = _y;                                                                    \
657     frame.size = image_size;                                                                \
658     [image drawInRect:frame fromRect:image_rect operation:mode fraction:1];
659
660 - (void)drawRect:(NSRect)rect
661 {
662     NSRect frame = [self frame];
663     NSRect image_rect;
664     NSSize image_size;
665     NSImage *img;
666     addImage(o_background_img, 0, 0, NSCompositeCopy);
667     addImage(o_vol_sld_img, 26, 23, NSCompositeSourceOver);
668     addImage(o_vol_mute_img, 16, 18, NSCompositeSourceOver);
669     addImage(o_vol_max_img, 124, 18, NSCompositeSourceOver);
670     addImage(o_time_sld_img, 15, 45, NSCompositeSourceOver);
671 }
672
673 @end
674
675 /*****************************************************************************
676  * VLCFSTimeSlider
677  *****************************************************************************/
678 @implementation VLCFSTimeSlider
679 - (void)drawKnobInRect:(NSRect)knobRect
680 {
681     NSRect image_rect;
682     NSImage *img = [NSImage imageNamed:@"fs_time_slider_knob_highlight"];
683     image_rect.size = [img size];
684     image_rect.origin.x = 0;
685     image_rect.origin.y = 0;
686     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
687     knobRect.size.width = image_rect.size.width;
688     knobRect.size.height = image_rect.size.height;
689     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
690 }
691
692 - (void)drawRect:(NSRect)rect
693 {
694     /* Draw default to make sure the slider behaves correctly */
695     [[NSGraphicsContext currentContext] saveGraphicsState];
696     NSRectClip(NSZeroRect);
697     [super drawRect:rect];
698     [[NSGraphicsContext currentContext] restoreGraphicsState];
699
700     NSRect knobRect = [[self cell] knobRectFlipped:NO];
701     knobRect.origin.y+=4;
702     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
703     [self drawKnobInRect: knobRect];
704 }
705
706 @end
707
708 /*****************************************************************************
709 * VLCFSVolumeSlider
710 *****************************************************************************/
711 @implementation VLCFSVolumeSlider
712
713 - (void)drawKnobInRect:(NSRect) knobRect
714 {
715     NSRect image_rect;
716     NSImage *img = [NSImage imageNamed:@"fs_volume_slider_knob_highlight"];
717     image_rect.size = [img size];
718     image_rect.origin.x = 0;
719     image_rect.origin.y = 0;
720     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
721     knobRect.size.width = image_rect.size.width;
722     knobRect.size.height = image_rect.size.height;
723     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
724 }
725
726 - (void)drawRect:(NSRect)rect
727 {
728     /* Draw default to make sure the slider behaves correctly */
729     [[NSGraphicsContext currentContext] saveGraphicsState];
730     NSRectClip(NSZeroRect);
731     [super drawRect:rect];
732     [[NSGraphicsContext currentContext] restoreGraphicsState];
733
734     [self drawFullVolumeMarker];
735
736     NSRect knobRect = [[self cell] knobRectFlipped:NO];
737     knobRect.origin.y+=7.5;
738     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
739     [self drawKnobInRect: knobRect];
740 }
741
742 - (void)drawFullVolBezierPath:(NSBezierPath*)bezierPath
743 {
744     CGFloat fullVolPos = [self fullVolumePos];
745     [bezierPath moveToPoint:NSMakePoint(fullVolPos, [self frame].size.height)];
746     [bezierPath lineToPoint:NSMakePoint(fullVolPos, 1.)];
747 }
748
749 @end
750