macosx: Set dark style of time slider for dark interface
[vlc.git] / modules / gui / macosx / VLCControlsBarCommon.m
1 /*****************************************************************************
2  * VLCControlsBarCommon.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012-2016 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul K├╝hne <fkuehne -at- videolan -dot- org>
8  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "VLCControlsBarCommon.h"
26 #import "VLCMain.h"
27 #import "VLCCoreInteraction.h"
28 #import "VLCMainMenu.h"
29 #import "VLCPlaylist.h"
30 #import "CompatibilityFixes.h"
31
32 /*****************************************************************************
33  * VLCControlsBarCommon
34  *
35  *  Holds all outlets, actions and code common for controls bar in detached
36  *  and in main window.
37  *****************************************************************************/
38
39 @interface VLCControlsBarCommon ()
40 {
41     NSImage * _pauseImage;
42     NSImage * _pressedPauseImage;
43     NSImage * _playImage;
44     NSImage * _pressedPlayImage;
45
46     NSTimeInterval last_fwd_event;
47     NSTimeInterval last_bwd_event;
48     BOOL just_triggered_next;
49     BOOL just_triggered_previous;
50 }
51 @end
52
53 @implementation VLCControlsBarCommon
54
55 - (void)awakeFromNib
56 {
57     _darkInterface = var_InheritBool(getIntf(), "macosx-interfacestyle");
58     _nativeFullscreenMode = var_InheritBool(getIntf(), "macosx-nativefullscreenmode");
59
60     [self.dropView setDrawBorder: NO];
61
62     [self.playButton setToolTip: _NS("Play/Pause")];
63     [[self.playButton cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
64     [[self.playButton cell] accessibilitySetOverrideValue:[self.playButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
65
66     [self.backwardButton setToolTip: _NS("Backward")];
67     [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
68     [[self.backwardButton cell] accessibilitySetOverrideValue:[self.backwardButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
69
70     [self.forwardButton setToolTip: _NS("Forward")];
71     [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
72     [[self.forwardButton cell] accessibilitySetOverrideValue:[self.forwardButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
73
74     [self.timeSlider setToolTip: _NS("Position")];
75     [[self.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];
76     [[self.timeSlider cell] accessibilitySetOverrideValue:[self.timeSlider toolTip] forAttribute:NSAccessibilityTitleAttribute];
77     if (_darkInterface)
78         [self.timeSlider setSliderStyleDark];
79
80     [self.fullscreenButton setToolTip: _NS("Toggle Fullscreen mode")];
81     [[self.fullscreenButton cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
82     [[self.fullscreenButton cell] accessibilitySetOverrideValue:[self.fullscreenButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
83
84     if (!_darkInterface) {
85         [self.bottomBarView setImagesLeft: imageFromRes(@"bottom-background") middle: imageFromRes(@"bottom-background") right: imageFromRes(@"bottom-background")];
86
87         [self.backwardButton setImage: imageFromRes(@"backward-3btns")];
88         [self.backwardButton setAlternateImage: imageFromRes(@"backward-3btns-pressed")];
89         _playImage = imageFromRes(@"play");
90         _pressedPlayImage = imageFromRes(@"play-pressed");
91         _pauseImage = imageFromRes(@"pause");
92         _pressedPauseImage = imageFromRes(@"pause-pressed");
93         [self.forwardButton setImage: imageFromRes(@"forward-3btns")];
94         [self.forwardButton setAlternateImage: imageFromRes(@"forward-3btns-pressed")];
95
96         [self.fullscreenButton setImage: imageFromRes(@"fullscreen-one-button")];
97         [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")];
98     } else {
99         [self.bottomBarView setImagesLeft: imageFromRes(@"bottomdark-left") middle: imageFromRes(@"bottom-background_dark") right: imageFromRes(@"bottomdark-right")];
100
101         [self.backwardButton setImage: imageFromRes(@"backward-3btns-dark")];
102         [self.backwardButton setAlternateImage: imageFromRes(@"backward-3btns-dark-pressed")];
103         _playImage = imageFromRes(@"play_dark");
104         _pressedPlayImage = imageFromRes(@"play-pressed_dark");
105         _pauseImage = imageFromRes(@"pause_dark");
106         _pressedPauseImage = imageFromRes(@"pause-pressed_dark");
107         [self.forwardButton setImage: imageFromRes(@"forward-3btns-dark")];
108         [self.forwardButton setAlternateImage: imageFromRes(@"forward-3btns-dark-pressed")];
109
110         [self.fullscreenButton setImage: imageFromRes(@"fullscreen-one-button-pressed_dark")];
111         [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed_dark")];
112     }
113
114     [self.playButton setImage: _playImage];
115     [self.playButton setAlternateImage: _pressedPlayImage];
116
117     NSColor *timeFieldTextColor;
118     if (!var_InheritBool(getIntf(), "macosx-interfacestyle"))
119         timeFieldTextColor = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
120     else
121         timeFieldTextColor = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
122     [self.timeField setTextColor: timeFieldTextColor];
123     [self.timeField setFont:[NSFont titleBarFontOfSize:10.0]];
124     [self.timeField setAlignment: NSCenterTextAlignment];
125     [self.timeField setNeedsDisplay:YES];
126     [self.timeField setRemainingIdentifier:@"DisplayTimeAsTimeRemaining"];
127
128     // remove fullscreen button for lion fullscreen
129     if (_nativeFullscreenMode) {
130         [self.fullscreenButton removeFromSuperview];
131     }
132
133     if (config_GetInt(getIntf(), "macosx-show-playback-buttons"))
134         [self toggleForwardBackwardMode: YES];
135
136 }
137
138 - (CGFloat)height
139 {
140     return [self.bottomBarView frame].size.height;
141 }
142
143 - (void)toggleForwardBackwardMode:(BOOL)b_alt
144 {
145     if (b_alt == YES) {
146         /* change the accessibility help for the backward/forward buttons accordingly */
147         [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
148         [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
149
150         [self.forwardButton setAction:@selector(alternateForward:)];
151         [self.backwardButton setAction:@selector(alternateBackward:)];
152
153     } else {
154         /* change the accessibility help for the backward/forward buttons accordingly */
155         [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
156         [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
157
158         [self.forwardButton setAction:@selector(fwd:)];
159         [self.backwardButton setAction:@selector(bwd:)];
160     }
161 }
162
163 #pragma mark -
164 #pragma mark Button Actions
165
166 - (IBAction)play:(id)sender
167 {
168     [[VLCCoreInteraction sharedInstance] playOrPause];
169 }
170
171 - (void)resetPreviousButton
172 {
173     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
174         // seems like no further event occurred, so let's switch the playback item
175         [[VLCCoreInteraction sharedInstance] previous];
176         just_triggered_previous = NO;
177     }
178 }
179
180 - (void)resetBackwardSkip
181 {
182     // the user stopped skipping, so let's allow him to change the item
183     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
184         just_triggered_previous = NO;
185 }
186
187 - (IBAction)bwd:(id)sender
188 {
189     if (!just_triggered_previous) {
190         just_triggered_previous = YES;
191         [self performSelector:@selector(resetPreviousButton)
192                    withObject: NULL
193                    afterDelay:0.40];
194     } else {
195         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
196             // we just skipped 4 "continous" events, otherwise we are too fast
197             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
198             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
199             [self performSelector:@selector(resetBackwardSkip)
200                        withObject: NULL
201                        afterDelay:0.40];
202         }
203     }
204 }
205
206 - (void)resetNextButton
207 {
208     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
209         // seems like no further event occurred, so let's switch the playback item
210         [[VLCCoreInteraction sharedInstance] next];
211         just_triggered_next = NO;
212     }
213 }
214
215 - (void)resetForwardSkip
216 {
217     // the user stopped skipping, so let's allow him to change the item
218     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
219         just_triggered_next = NO;
220 }
221
222 - (IBAction)fwd:(id)sender
223 {
224     if (!just_triggered_next) {
225         just_triggered_next = YES;
226         [self performSelector:@selector(resetNextButton)
227                    withObject: NULL
228                    afterDelay:0.40];
229     } else {
230         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
231             // we just skipped 4 "continous" events, otherwise we are too fast
232             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
233             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
234             [self performSelector:@selector(resetForwardSkip)
235                        withObject: NULL
236                        afterDelay:0.40];
237         }
238     }
239 }
240
241 // alternative actions for forward / backward buttons when next / prev are activated
242 - (IBAction)alternateForward:(id)sender
243 {
244     [[VLCCoreInteraction sharedInstance] forwardExtraShort];
245 }
246
247 - (IBAction)alternateBackward:(id)sender
248 {
249     [[VLCCoreInteraction sharedInstance] backwardExtraShort];
250 }
251
252 - (IBAction)timeSliderAction:(id)sender
253 {
254     float f_updated;
255     input_thread_t * p_input;
256
257     switch([[NSApp currentEvent] type]) {
258         case NSLeftMouseUp:
259             /* Ignore mouse up, as this is a continous slider and
260              * when the user does a single click to a position on the slider,
261              * the action is called twice, once for the mouse down and once
262              * for the mouse up event. This results in two short seeks one
263              * after another to the same position, which results in weird
264              * audio quirks.
265              */
266             return;
267         case NSLeftMouseDown:
268         case NSLeftMouseDragged:
269             f_updated = [sender floatValue];
270             break;
271
272         default:
273             return;
274     }
275     p_input = pl_CurrentInput(getIntf());
276     if (p_input != NULL) {
277         vlc_value_t pos;
278         NSString * o_time;
279
280         pos.f_float = f_updated / 10000.;
281         var_Set(p_input, "position", pos);
282         [self.timeSlider setFloatValue: f_updated];
283
284         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[self.timeField timeRemaining]];
285         [self.timeField setStringValue: o_time];
286         vlc_object_release(p_input);
287     }
288 }
289
290 - (IBAction)fullscreen:(id)sender
291 {
292     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
293 }
294
295 #pragma mark -
296 #pragma mark Updaters
297
298 - (void)updateTimeSlider
299 {
300     input_thread_t * p_input;
301     p_input = pl_CurrentInput(getIntf());
302
303     [self.timeSlider setHidden:NO];
304
305     if (!p_input) {
306         // Nothing playing
307         [self.timeSlider setFloatValue: 0.0];
308         [self.timeField setStringValue: @"00:00"];
309         [self.timeSlider setIndefinite:NO];
310         [self.timeSlider setEnabled:NO];
311         return;
312     }
313
314     vlc_value_t pos;
315     var_Get(p_input, "position", &pos);
316     [self.timeSlider setFloatValue:(10000. * pos.f_float)];
317
318     mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
319     if (dur == -1) {
320         // No duration, disable slider
321         [self.timeSlider setEnabled:NO];
322     } else {
323         input_state_e inputState = input_GetState(p_input);
324         bool buffering = (inputState == INIT_S || inputState == OPENING_S);
325         [self.timeSlider setIndefinite:buffering];
326     }
327
328     NSString *time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString:p_input
329                                                                       negative:[self.timeField timeRemaining]];
330     [self.timeField setStringValue:time];
331     [self.timeField setNeedsDisplay:YES];
332
333     vlc_object_release(p_input);
334 }
335
336 - (void)updateControls
337 {
338     bool b_plmul = false;
339     bool b_seekable = false;
340     bool b_chapters = false;
341     bool b_buffering = false;
342
343     playlist_t * p_playlist = pl_Get(getIntf());
344
345     PL_LOCK;
346     b_plmul = playlist_CurrentSize(p_playlist) > 1;
347     PL_UNLOCK;
348
349     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
350
351     if (p_input) {
352         input_state_e inputState = input_GetState(p_input);
353         if (inputState == INIT_S || inputState == OPENING_S)
354             b_buffering = YES;
355
356         /* seekable streams */
357         b_seekable = var_GetBool(p_input, "can-seek");
358
359         /* chapters & titles */
360         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
361
362         vlc_object_release(p_input);
363     }
364
365     [self.timeSlider setEnabled: b_seekable];
366
367     [self.forwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
368     [self.backwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
369 }
370
371 - (void)setPause
372 {
373     [self.playButton setImage: _pauseImage];
374     [self.playButton setAlternateImage: _pressedPauseImage];
375     [self.playButton setToolTip: _NS("Pause")];
376 }
377
378 - (void)setPlay
379 {
380     [self.playButton setImage: _playImage];
381     [self.playButton setAlternateImage: _pressedPlayImage];
382     [self.playButton setToolTip: _NS("Play")];
383 }
384
385 - (void)setFullscreenState:(BOOL)b_fullscreen
386 {
387     if (!self.nativeFullscreenMode)
388         [self.fullscreenButton setState:b_fullscreen];
389 }
390
391 @end