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