1 /*****************************************************************************
2 * ControlsBar.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"
26 #import "VLCMainWindowControlsBar.h"
28 #import "VLCCoreInteraction.h"
29 #import "VLCMainMenu.h"
30 #import "VLCPlaylist.h"
31 #import "CompatibilityFixes.h"
34 /*****************************************************************************
35 * VLCMainWindowControlsBar
37 * Holds all specific outlets, actions and code for the main window controls bar.
38 *****************************************************************************/
40 @interface VLCMainWindowControlsBar()
42 NSImage * _repeatImage;
43 NSImage * _pressedRepeatImage;
44 NSImage * _repeatAllImage;
45 NSImage * _pressedRepeatAllImage;
46 NSImage * _repeatOneImage;
47 NSImage * _pressedRepeatOneImage;
48 NSImage * _shuffleImage;
49 NSImage * _pressedShuffleImage;
50 NSImage * _shuffleOnImage;
51 NSImage * _pressedShuffleOnImage;
56 @implementation VLCMainWindowControlsBar
62 [self.stopButton setToolTip: _NS("Stop")];
63 self.stopButton.accessibilityLabel = self.stopButton.toolTip;
65 [self.playlistButton setToolTip: _NS("Show/Hide Playlist")];
66 self.playlistButton.accessibilityLabel = self.playlistButton.toolTip;
68 [self.repeatButton setToolTip: _NS("Repeat")];
69 self.repeatButton.accessibilityLabel = _NS("Change repeat mode. Modes: repeat one, repeat all and no repeat.");
70 self.repeatButton.accessibilityTitle = self.repeatButton.toolTip;
72 [self.shuffleButton setToolTip: _NS("Shuffle")];
73 self.shuffleButton.accessibilityLabel = self.shuffleButton.toolTip;
75 NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), 100];
76 [self.volumeSlider setToolTip: volumeTooltip];
77 self.volumeSlider.accessibilityLabel = _NS("Volume");
79 [self.volumeDownButton setToolTip: _NS("Mute")];
80 self.volumeDownButton.accessibilityLabel = self.volumeDownButton.toolTip;
82 [self.volumeUpButton setToolTip: _NS("Full Volume")];
83 self.volumeUpButton.accessibilityLabel = self.volumeUpButton.toolTip;
85 [self.effectsButton setToolTip: _NS("Audio Effects")];
86 self.effectsButton.accessibilityTitle = _NS("Open Audio Effects window");
87 self.effectsButton.accessibilityLabel = self.effectsButton.toolTip;
89 [self.stopButton setImage: imageFromRes(@"stop")];
90 [self.stopButton setAlternateImage: imageFromRes(@"stop-pressed")];
92 [self.playlistButton setImage: imageFromRes(@"playlist-btn")];
93 [self.playlistButton setAlternateImage: imageFromRes(@"playlist-btn-pressed")];
94 _repeatImage = imageFromRes(@"repeat");
95 _pressedRepeatImage = imageFromRes(@"repeat-pressed");
96 _repeatAllImage = imageFromRes(@"repeat-all");
97 _pressedRepeatAllImage = imageFromRes(@"repeat-all-pressed");
98 _repeatOneImage = imageFromRes(@"repeat-one");
99 _pressedRepeatOneImage = imageFromRes(@"repeat-one-pressed");
100 _shuffleImage = imageFromRes(@"shuffle");
101 _pressedShuffleImage = imageFromRes(@"shuffle-pressed");
102 _shuffleOnImage = imageFromRes(@"shuffle-blue");
103 _pressedShuffleOnImage = imageFromRes(@"shuffle-blue-pressed");
105 [self.volumeDownButton setImage: imageFromRes(@"volume-low")];
106 [self.volumeUpButton setImage: imageFromRes(@"volume-high")];
107 [self.volumeSlider setUsesBrightArtwork: YES];
109 if (self.nativeFullscreenMode) {
110 [self.effectsButton setImage: imageFromRes(@"effects-one-button")];
111 [self.effectsButton setAlternateImage: imageFromRes(@"effects-one-button-pressed")];
113 [self.effectsButton setImage: imageFromRes(@"effects-double-buttons")];
114 [self.effectsButton setAlternateImage: imageFromRes(@"effects-double-buttons-pressed")];
117 [self.fullscreenButton setImage: imageFromRes(@"fullscreen-double-buttons")];
118 [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")];
120 [self.prevButton setImage: imageFromRes(@"previous-6btns")];
121 [self.prevButton setAlternateImage: imageFromRes(@"previous-6btns-pressed")];
122 [self.nextButton setImage: imageFromRes(@"next-6btns")];
123 [self.nextButton setAlternateImage: imageFromRes(@"next-6btns-pressed")];
124 [self.repeatButton setImage: _repeatImage];
125 [self.repeatButton setAlternateImage: _pressedRepeatImage];
126 [self.shuffleButton setImage: _shuffleImage];
127 [self.shuffleButton setAlternateImage: _pressedShuffleImage];
129 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
130 [self.volumeSlider setEnabled: b_mute];
131 [self.volumeSlider setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]];
132 [self.volumeSlider setDefaultValue: AOUT_VOLUME_DEFAULT];
133 [self.volumeUpButton setEnabled: b_mute];
135 // configure optional buttons
136 if (!var_InheritBool(getIntf(), "macosx-show-effects-button"))
137 [self removeEffectsButton:NO];
139 if (!var_InheritBool(getIntf(), "macosx-show-playmode-buttons"))
140 [self removePlaymodeButtons:NO];
142 if (!var_InheritBool(getIntf(), "macosx-show-playback-buttons"))
143 [self removeJumpButtons:NO];
145 [[[VLCMain sharedInstance] playlist] playbackModeUpdated];
150 #pragma mark interface customization
153 - (void)hideButtonWithConstraint:(NSLayoutConstraint *)constraint animation:(BOOL)animation
155 NSAssert([constraint.firstItem isKindOfClass:[NSButton class]], @"Constraint must be for NSButton object");
157 NSLayoutConstraint *animatedConstraint = animation ? constraint.animator : constraint;
158 animatedConstraint.constant = 0;
161 - (void)showButtonWithConstraint:(NSLayoutConstraint *)constraint animation:(BOOL)animation
163 NSAssert([constraint.firstItem isKindOfClass:[NSButton class]], @"Constraint must be for NSButton object");
165 NSLayoutConstraint *animatedConstraint = animation ? constraint.animator : constraint;
166 animatedConstraint.constant = ((NSButton *)constraint.firstItem).image.size.width;
169 - (void)toggleEffectsButton
171 if (var_InheritBool(getIntf(), "macosx-show-effects-button"))
172 [self addEffectsButton:YES];
174 [self removeEffectsButton:YES];
177 - (void)addEffectsButton:(BOOL)withAnimation
179 [NSAnimationContext beginGrouping];
180 [self showButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation];
182 id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton;
183 if (!self.nativeFullscreenMode) {
184 [button setImage: imageFromRes(@"fullscreen-double-buttons")];
185 [button setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")];
187 [NSAnimationContext endGrouping];
190 - (void)removeEffectsButton:(BOOL)withAnimation
192 [NSAnimationContext beginGrouping];
193 [self hideButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation];
195 id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton;
196 if (!self.nativeFullscreenMode) {
197 [button setImage: imageFromRes(@"fullscreen-one-button")];
198 [button setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")];
200 [NSAnimationContext endGrouping];
203 - (void)toggleJumpButtons
205 if (var_InheritBool(getIntf(), "macosx-show-playback-buttons"))
206 [self addJumpButtons:YES];
208 [self removeJumpButtons:YES];
211 - (void)addJumpButtons:(BOOL)withAnimation
213 [NSAnimationContext beginGrouping];
214 [self showButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation];
215 [self showButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation];
217 id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton;
218 id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton;
219 [forwardButton setImage:imageFromRes(@"forward-6btns")];
220 [forwardButton setAlternateImage:imageFromRes(@"forward-6btns-pressed")];
221 [backwardButton setImage:imageFromRes(@"backward-6btns")];
222 [backwardButton setAlternateImage:imageFromRes(@"backward-6btns-pressed")];
224 [NSAnimationContext endGrouping];
226 [self toggleForwardBackwardMode: YES];
229 - (void)removeJumpButtons:(BOOL)withAnimation
231 [NSAnimationContext beginGrouping];
232 [self hideButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation];
233 [self hideButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation];
235 id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton;
236 id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton;
237 [forwardButton setImage:imageFromRes(@"forward-3btns")];
238 [forwardButton setAlternateImage:imageFromRes(@"forward-3btns-pressed")];
239 [backwardButton setImage:imageFromRes(@"backward-3btns")];
240 [backwardButton setAlternateImage:imageFromRes(@"backward-3btns-pressed")];
241 [NSAnimationContext endGrouping];
243 [self toggleForwardBackwardMode: NO];
246 - (void)togglePlaymodeButtons
248 if (var_InheritBool(getIntf(), "macosx-show-playmode-buttons"))
249 [self addPlaymodeButtons:YES];
251 [self removePlaymodeButtons:YES];
254 - (void)addPlaymodeButtons:(BOOL)withAnimation
256 [NSAnimationContext beginGrouping];
257 [self showButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation];
258 [self showButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation];
260 id button = withAnimation ? self.playlistButton.animator : self.playlistButton;
261 [button setImage:imageFromRes(@"playlist-btn")];
262 [button setAlternateImage:imageFromRes(@"playlist-btn-pressed")];
263 [NSAnimationContext endGrouping];
266 - (void)removePlaymodeButtons:(BOOL)withAnimation
268 [NSAnimationContext beginGrouping];
270 [self hideButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation];
271 [self hideButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation];
273 id button = withAnimation ? self.playlistButton.animator : self.playlistButton;
274 [button setImage:imageFromRes(@"playlist-1btn")];
275 [button setAlternateImage:imageFromRes(@"playlist-1btn-pressed")];
276 [NSAnimationContext endGrouping];
280 #pragma mark Extra button actions
282 - (IBAction)stop:(id)sender
284 [[VLCCoreInteraction sharedInstance] stop];
287 // dynamically created next / prev buttons
288 - (IBAction)prev:(id)sender
290 [[VLCCoreInteraction sharedInstance] previous];
293 - (IBAction)next:(id)sender
295 [[VLCCoreInteraction sharedInstance] next];
300 [self.repeatButton setImage: _repeatOneImage];
301 [self.repeatButton setAlternateImage: _pressedRepeatOneImage];
306 [self.repeatButton setImage: _repeatAllImage];
307 [self.repeatButton setAlternateImage: _pressedRepeatAllImage];
312 [self.repeatButton setImage: _repeatImage];
313 [self.repeatButton setAlternateImage: _pressedRepeatImage];
316 - (IBAction)repeat:(id)sender
318 vlc_value_t looping,repeating;
319 intf_thread_t * p_intf = getIntf();
320 playlist_t * p_playlist = pl_Get(p_intf);
322 var_Get(p_playlist, "repeat", &repeating);
323 var_Get(p_playlist, "loop", &looping);
325 if (!repeating.b_bool && !looping.b_bool) {
326 /* was: no repeating at all, switching to Repeat One */
327 [[VLCCoreInteraction sharedInstance] repeatOne];
330 else if (repeating.b_bool && !looping.b_bool) {
331 /* was: Repeat One, switching to Repeat All */
332 [[VLCCoreInteraction sharedInstance] repeatAll];
335 /* was: Repeat All or bug in VLC, switching to Repeat Off */
336 [[VLCCoreInteraction sharedInstance] repeatOff];
344 playlist_t *p_playlist = pl_Get(getIntf());
345 b_value = var_GetBool(p_playlist, "random");
348 [self.shuffleButton setImage: _shuffleOnImage];
349 [self.shuffleButton setAlternateImage: _pressedShuffleOnImage];
351 [self.shuffleButton setImage: _shuffleImage];
352 [self.shuffleButton setAlternateImage: _pressedShuffleImage];
356 - (IBAction)shuffle:(id)sender
358 [[VLCCoreInteraction sharedInstance] shuffle];
362 - (IBAction)togglePlaylist:(id)sender
364 [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent];
367 - (IBAction)volumeAction:(id)sender
369 if (sender == self.volumeSlider)
370 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
371 else if (sender == self.volumeDownButton)
372 [[VLCCoreInteraction sharedInstance] toggleMute];
374 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
377 - (IBAction)effects:(id)sender
379 [[[VLCMain sharedInstance] mainMenu] showAudioEffects: sender];
383 #pragma mark Extra updaters
385 - (void)updateVolumeSlider
387 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
388 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
393 [self.volumeSlider setIntValue: i_volume];
395 i_volume = (i_volume * 200) / AOUT_VOLUME_MAX;
396 NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), i_volume];
397 [self.volumeSlider setToolTip:volumeTooltip];
399 [self.volumeSlider setEnabled: !b_muted];
400 [self.volumeUpButton setEnabled: !b_muted];
403 - (void)updateControls
405 [super updateControls];
407 bool b_input = false;
408 bool b_seekable = false;
409 bool b_plmul = false;
410 bool b_control = false;
411 bool b_chapters = false;
413 playlist_t * p_playlist = pl_Get(getIntf());
416 b_plmul = playlist_CurrentSize(p_playlist) > 1;
419 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
420 if ((b_input = (p_input != NULL))) {
421 /* seekable streams */
422 b_seekable = var_GetBool(p_input, "can-seek");
424 /* check whether slow/fast motion is possible */
425 b_control = var_GetBool(p_input, "can-rate");
427 /* chapters & titles */
428 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
430 vlc_object_release(p_input);
433 [self.stopButton setEnabled: b_input];
434 [self.prevButton setEnabled: (b_seekable || b_plmul || b_chapters)];
435 [self.nextButton setEnabled: (b_seekable || b_plmul || b_chapters)];
437 [[[VLCMain sharedInstance] mainMenu] setRateControlsEnabled: b_control];