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