1 /*****************************************************************************
2 * VLCControlsBarCommon.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2012-2016 VLC authors and VideoLAN
7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8 * David Fuhrmann <david dot fuhrmann at googlemail dot com>
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.
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.
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 *****************************************************************************/
25 #import "VLCControlsBarCommon.h"
27 #import "VLCCoreInteraction.h"
28 #import "VLCMainMenu.h"
29 #import "VLCPlaylist.h"
30 #import "CompatibilityFixes.h"
32 /*****************************************************************************
33 * VLCControlsBarCommon
35 * Holds all outlets, actions and code common for controls bar in detached
37 *****************************************************************************/
39 @interface VLCControlsBarCommon ()
41 NSImage * _pauseImage;
42 NSImage * _pressedPauseImage;
44 NSImage * _pressedPlayImage;
46 NSTimeInterval last_fwd_event;
47 NSTimeInterval last_bwd_event;
48 BOOL just_triggered_next;
49 BOOL just_triggered_previous;
53 @implementation VLCControlsBarCommon
59 _nativeFullscreenMode = var_InheritBool(getIntf(), "macosx-nativefullscreenmode");
61 [self.dropView setDrawBorder: NO];
63 [self.playButton setToolTip: _NS("Play")];
64 self.playButton.accessibilityLabel = self.playButton.toolTip;
66 [self.backwardButton setToolTip: _NS("Backward")];
67 self.backwardButton.accessibilityLabel = _NS("Seek backward");
68 self.backwardButton.accessibilityTitle = self.backwardButton.toolTip;
70 [self.forwardButton setToolTip: _NS("Forward")];
71 self.forwardButton.accessibilityLabel = _NS("Seek forward");
72 self.forwardButton.accessibilityTitle = self.forwardButton.toolTip;
74 [self.timeSlider setToolTip: _NS("Position")];
75 self.timeSlider.accessibilityLabel = _NS("Playback position");
76 self.timeSlider.accessibilityTitle = self.timeSlider.toolTip;
78 [self.fullscreenButton setToolTip: _NS("Enter fullscreen")];
79 self.fullscreenButton.accessibilityLabel = self.fullscreenButton.toolTip;
81 [self.backwardButton setImage: imageFromRes(@"backward-3btns")];
82 [self.backwardButton setAlternateImage: imageFromRes(@"backward-3btns-pressed")];
83 _playImage = imageFromRes(@"play");
84 _pressedPlayImage = imageFromRes(@"play-pressed");
85 _pauseImage = imageFromRes(@"pause");
86 _pressedPauseImage = imageFromRes(@"pause-pressed");
87 [self.forwardButton setImage: imageFromRes(@"forward-3btns")];
88 [self.forwardButton setAlternateImage: imageFromRes(@"forward-3btns-pressed")];
90 [self.fullscreenButton setImage: imageFromRes(@"fullscreen-one-button")];
91 [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")];
93 [self.playButton setImage: _playImage];
94 [self.playButton setAlternateImage: _pressedPlayImage];
96 NSColor *timeFieldTextColor = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
97 [self.timeField setTextColor: timeFieldTextColor];
98 [self.timeField setFont:[NSFont titleBarFontOfSize:10.0]];
99 [self.timeField setAlignment: NSCenterTextAlignment];
100 [self.timeField setNeedsDisplay:YES];
101 [self.timeField setRemainingIdentifier:@"DisplayTimeAsTimeRemaining"];
102 self.timeField.accessibilityLabel = _NS("Playback time");
104 // remove fullscreen button for lion fullscreen
105 if (_nativeFullscreenMode) {
106 self.fullscreenButtonWidthConstraint.constant = 0;
109 if (config_GetInt("macosx-show-playback-buttons"))
110 [self toggleForwardBackwardMode: YES];
115 return [self.bottomBarView frame].size.height;
118 - (void)toggleForwardBackwardMode:(BOOL)b_alt
121 /* change the accessibility help for the backward/forward buttons accordingly */
122 self.backwardButton.accessibilityTitle = _NS("Backward");
123 self.backwardButton.accessibilityLabel = _NS("Seek backward");
125 self.forwardButton.accessibilityTitle = _NS("Forward");
126 self.forwardButton.accessibilityLabel = _NS("Seek forward");
128 [self.forwardButton setAction:@selector(alternateForward:)];
129 [self.backwardButton setAction:@selector(alternateBackward:)];
132 /* change the accessibility help for the backward/forward buttons accordingly */
133 self.backwardButton.accessibilityTitle = _NS("Previous");
134 self.backwardButton.accessibilityLabel = _NS("Go to previous item");
136 self.forwardButton.accessibilityTitle = _NS("Next");
137 self.forwardButton.accessibilityLabel = _NS("Go to next item");
139 [self.forwardButton setAction:@selector(fwd:)];
140 [self.backwardButton setAction:@selector(bwd:)];
145 #pragma mark Button Actions
147 - (IBAction)play:(id)sender
149 [[VLCCoreInteraction sharedInstance] playOrPause];
152 - (void)resetPreviousButton
154 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
155 // seems like no further event occurred, so let's switch the playback item
156 [[VLCCoreInteraction sharedInstance] previous];
157 just_triggered_previous = NO;
161 - (void)resetBackwardSkip
163 // the user stopped skipping, so let's allow him to change the item
164 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
165 just_triggered_previous = NO;
168 - (IBAction)bwd:(id)sender
170 if (!just_triggered_previous) {
171 just_triggered_previous = YES;
172 [self performSelector:@selector(resetPreviousButton)
176 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
177 // we just skipped 4 "continous" events, otherwise we are too fast
178 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
179 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
180 [self performSelector:@selector(resetBackwardSkip)
187 - (void)resetNextButton
189 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
190 // seems like no further event occurred, so let's switch the playback item
191 [[VLCCoreInteraction sharedInstance] next];
192 just_triggered_next = NO;
196 - (void)resetForwardSkip
198 // the user stopped skipping, so let's allow him to change the item
199 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
200 just_triggered_next = NO;
203 - (IBAction)fwd:(id)sender
205 if (!just_triggered_next) {
206 just_triggered_next = YES;
207 [self performSelector:@selector(resetNextButton)
211 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
212 // we just skipped 4 "continous" events, otherwise we are too fast
213 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
214 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
215 [self performSelector:@selector(resetForwardSkip)
222 // alternative actions for forward / backward buttons when next / prev are activated
223 - (IBAction)alternateForward:(id)sender
225 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
228 - (IBAction)alternateBackward:(id)sender
230 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
233 - (IBAction)timeSliderAction:(id)sender
236 input_thread_t * p_input;
238 switch([[NSApp currentEvent] type]) {
240 /* Ignore mouse up, as this is a continous slider and
241 * when the user does a single click to a position on the slider,
242 * the action is called twice, once for the mouse down and once
243 * for the mouse up event. This results in two short seeks one
244 * after another to the same position, which results in weird
248 case NSLeftMouseDown:
249 case NSLeftMouseDragged:
250 f_updated = [sender floatValue];
253 f_updated = [sender floatValue];
259 p_input = pl_CurrentInput(getIntf());
260 if (p_input != NULL) {
264 pos.f_float = f_updated / 10000.;
265 var_Set(p_input, "position", pos);
266 [self.timeSlider setFloatValue: f_updated];
268 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[self.timeField timeRemaining]];
269 [self.timeField setStringValue: o_time];
270 vlc_object_release(p_input);
274 - (IBAction)fullscreen:(id)sender
276 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
280 #pragma mark Updaters
282 - (void)updateTimeSlider
284 input_thread_t * p_input;
285 p_input = pl_CurrentInput(getIntf());
287 [self.timeSlider setHidden:NO];
291 [self.timeSlider setKnobHidden:YES];
292 [self.timeSlider setFloatValue: 0.0];
293 [self.timeField setStringValue: @"00:00"];
294 [self.timeSlider setIndefinite:NO];
295 [self.timeSlider setEnabled:NO];
299 [self.timeSlider setKnobHidden:NO];
302 var_Get(p_input, "position", &pos);
303 [self.timeSlider setFloatValue:(10000. * pos.f_float)];
305 vlc_tick_t dur = input_item_GetDuration(input_GetItem(p_input));
307 // No duration, disable slider
308 [self.timeSlider setEnabled:NO];
310 input_state_e inputState = var_GetInteger(p_input, "state");
311 bool buffering = (inputState == INIT_S || inputState == OPENING_S);
312 [self.timeSlider setIndefinite:buffering];
315 NSString *time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString:p_input
316 negative:[self.timeField timeRemaining]];
317 [self.timeField setStringValue:time];
318 [self.timeField setNeedsDisplay:YES];
320 vlc_object_release(p_input);
323 - (void)updateControls
325 bool b_plmul = false;
326 bool b_seekable = false;
327 bool b_chapters = false;
329 playlist_t * p_playlist = pl_Get(getIntf());
332 b_plmul = playlist_CurrentSize(p_playlist) > 1;
335 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
338 /* seekable streams */
339 b_seekable = var_GetBool(p_input, "can-seek");
341 /* chapters & titles */
342 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
344 vlc_object_release(p_input);
347 [self.timeSlider setEnabled: b_seekable];
349 [self.forwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
350 [self.backwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
355 [self.playButton setImage: _pauseImage];
356 [self.playButton setAlternateImage: _pressedPauseImage];
357 [self.playButton setToolTip: _NS("Pause")];
358 self.playButton.accessibilityLabel = self.playButton.toolTip;
363 [self.playButton setImage: _playImage];
364 [self.playButton setAlternateImage: _pressedPlayImage];
365 [self.playButton setToolTip: _NS("Play")];
366 self.playButton.accessibilityLabel = self.playButton.toolTip;
369 - (void)setFullscreenState:(BOOL)b_fullscreen
371 if (!self.nativeFullscreenMode)
372 [self.fullscreenButton setState:b_fullscreen];