/***************************************************************************** * ControlsBar.m: MacOS X interface module ***************************************************************************** * Copyright (C) 2012-2018 VLC authors and VideoLAN * $Id$ * * Authors: Felix Paul Kühne * David Fuhrmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #import "VLCControlsBarCommon.h" #import "VLCMainWindowControlsBar.h" #import "VLCMain.h" #import "VLCCoreInteraction.h" #import "VLCMainMenu.h" #import "VLCPlaylist.h" #import "CompatibilityFixes.h" /***************************************************************************** * VLCMainWindowControlsBar * * Holds all specific outlets, actions and code for the main window controls bar. *****************************************************************************/ @interface VLCMainWindowControlsBar() { NSImage * _repeatImage; NSImage * _pressedRepeatImage; NSImage * _repeatAllImage; NSImage * _pressedRepeatAllImage; NSImage * _repeatOneImage; NSImage * _pressedRepeatOneImage; NSImage * _shuffleImage; NSImage * _pressedShuffleImage; NSImage * _shuffleOnImage; NSImage * _pressedShuffleOnImage; BOOL _shuffle; BOOL _repeatOne; BOOL _repeatAll; } @end @implementation VLCMainWindowControlsBar - (void)awakeFromNib { [super awakeFromNib]; [self.stopButton setToolTip: _NS("Stop")]; [[self.stopButton cell] accessibilitySetOverrideValue:[self.stopButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute]; [self.playlistButton setToolTip: _NS("Show/Hide Playlist")]; [[self.playlistButton cell] accessibilitySetOverrideValue:[self.playlistButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute]; [self.repeatButton setToolTip: _NS("Repeat")]; [[self.repeatButton cell] accessibilitySetOverrideValue:_NS("Change repeat mode. Modes: repeat one, repeat all and no repeat.") forAttribute:NSAccessibilityDescriptionAttribute]; [[self.repeatButton cell] accessibilitySetOverrideValue:[self.repeatButton toolTip] forAttribute:NSAccessibilityTitleAttribute]; [self.shuffleButton setToolTip: _NS("Shuffle")]; [[self.shuffleButton cell] accessibilitySetOverrideValue:[self.shuffleButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute]; NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), 100]; [self.volumeSlider setToolTip: volumeTooltip]; [[self.volumeSlider cell] accessibilitySetOverrideValue:_NS("Volume") forAttribute:NSAccessibilityDescriptionAttribute]; [self.volumeDownButton setToolTip: _NS("Mute")]; [[self.volumeDownButton cell] accessibilitySetOverrideValue:[self.volumeDownButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute]; [self.volumeUpButton setToolTip: _NS("Full Volume")]; [[self.volumeUpButton cell] accessibilitySetOverrideValue:[self.volumeUpButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute]; [self.effectsButton setToolTip: _NS("Audio Effects")]; [[self.effectsButton cell] accessibilitySetOverrideValue:_NS("Open Audio Effects window") forAttribute:NSAccessibilityDescriptionAttribute]; [[self.effectsButton cell] accessibilitySetOverrideValue:[self.effectsButton toolTip] forAttribute:NSAccessibilityTitleAttribute]; if (!self.darkInterface) { [self setBrightButtonImageSet]; } else { [self setDarkButtonImageSet]; } BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute]; [self.volumeSlider setEnabled: b_mute]; [self.volumeSlider setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]]; [self.volumeSlider setDefaultValue: AOUT_VOLUME_DEFAULT]; [self.volumeUpButton setEnabled: b_mute]; [[[VLCMain sharedInstance] playlist] playbackModeUpdated]; } - (void)setBrightButtonImageSet { [super setBrightButtonImageSet]; [self.stopButton setImage: imageFromRes(@"stop")]; [self.stopButton setAlternateImage: imageFromRes(@"stop-pressed")]; _repeatImage = imageFromRes(@"repeat"); _pressedRepeatImage = imageFromRes(@"repeat-pressed"); _repeatAllImage = imageFromRes(@"repeat-all"); _pressedRepeatAllImage = imageFromRes(@"repeat-all-pressed"); _repeatOneImage = imageFromRes(@"repeat-one"); _pressedRepeatOneImage = imageFromRes(@"repeat-one-pressed"); _shuffleImage = imageFromRes(@"shuffle"); _pressedShuffleImage = imageFromRes(@"shuffle-pressed"); _shuffleOnImage = imageFromRes(@"shuffle-blue"); _pressedShuffleOnImage = imageFromRes(@"shuffle-blue-pressed"); [self.volumeDownButton setImage: imageFromRes(@"volume-low")]; [self.volumeUpButton setImage: imageFromRes(@"volume-high")]; [self.volumeSlider setUsesBrightArtwork: YES]; if (self.nativeFullscreenMode) { [self.effectsButton setImage: imageFromRes(@"effects-one-button")]; [self.effectsButton setAlternateImage: imageFromRes(@"effects-one-button-pressed")]; } else { [self.effectsButton setImage: imageFromRes(@"effects-double-buttons")]; [self.effectsButton setAlternateImage: imageFromRes(@"effects-double-buttons-pressed")]; } [self.fullscreenButton setImage: imageFromRes(@"fullscreen-double-buttons")]; [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")]; [self.prevButton setImage: imageFromRes(@"previous-6btns")]; [self.prevButton setAlternateImage: imageFromRes(@"previous-6btns-pressed")]; [self.nextButton setImage: imageFromRes(@"next-6btns")]; [self.nextButton setAlternateImage: imageFromRes(@"next-6btns-pressed")]; [self updatePlaymodeButtonImages]; [self setupJumpButtons:NO]; [self setupPlaymodeButtons:NO]; [self setupEffectsButton:NO]; } - (void)setDarkButtonImageSet { [super setDarkButtonImageSet]; [self.stopButton setImage: imageFromRes(@"stop_dark")]; [self.stopButton setAlternateImage: imageFromRes(@"stop-pressed_dark")]; _repeatImage = imageFromRes(@"repeat_dark"); _pressedRepeatImage = imageFromRes(@"repeat-pressed_dark"); _repeatAllImage = imageFromRes(@"repeat-all-blue_dark"); _pressedRepeatAllImage = imageFromRes(@"repeat-all-blue-pressed_dark"); _repeatOneImage = imageFromRes(@"repeat-one-blue_dark"); _pressedRepeatOneImage = imageFromRes(@"repeat-one-blue-pressed_dark"); _shuffleImage = imageFromRes(@"shuffle_dark"); _pressedShuffleImage = imageFromRes(@"shuffle-pressed_dark"); _shuffleOnImage = imageFromRes(@"shuffle-blue_dark"); _pressedShuffleOnImage = imageFromRes(@"shuffle-blue-pressed_dark"); [self.volumeDownButton setImage: imageFromRes(@"volume-low_dark")]; [self.volumeUpButton setImage: imageFromRes(@"volume-high_dark")]; [self.volumeSlider setUsesBrightArtwork: NO]; if (self.nativeFullscreenMode) { [self.effectsButton setImage: imageFromRes(@"effects-one-button_dark")]; [self.effectsButton setAlternateImage: imageFromRes(@"effects-one-button-pressed-dark")]; } else { [self.effectsButton setImage: imageFromRes(@"effects-double-buttons_dark")]; [self.effectsButton setAlternateImage: imageFromRes(@"effects-double-buttons-pressed_dark")]; } [self.fullscreenButton setImage: imageFromRes(@"fullscreen-double-buttons_dark")]; [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed_dark")]; [self.prevButton setImage: imageFromRes(@"previous-6btns-dark")]; [self.prevButton setAlternateImage: imageFromRes(@"previous-6btns-dark-pressed")]; [self.nextButton setImage: imageFromRes(@"next-6btns-dark")]; [self.nextButton setAlternateImage: imageFromRes(@"next-6btns-dark-pressed")]; [self updatePlaymodeButtonImages]; [self setupJumpButtons:NO]; [self setupPlaymodeButtons:NO]; [self setupEffectsButton:NO]; } - (void)updatePlaymodeButtonImages { if (_shuffle) { self.shuffleButton.image = _shuffleOnImage; self.shuffleButton.alternateImage = _pressedShuffleImage; } else { self.shuffleButton.image = _shuffleImage; self.shuffleButton.alternateImage = _pressedShuffleImage; } if (_repeatOne) { self.repeatButton.image = _repeatOneImage; self.repeatButton.alternateImage = _pressedRepeatOneImage; } else if (_repeatAll) { self.repeatButton.image = _repeatAllImage; self.repeatButton.alternateImage = _pressedRepeatAllImage; } else { self.repeatButton.image = _repeatImage; self.repeatButton.alternateImage = _pressedRepeatImage; } } #pragma mark - #pragma mark interface customization - (void)hideButtonWithConstraint:(NSLayoutConstraint *)constraint animation:(BOOL)animation { NSAssert([constraint.firstItem isKindOfClass:[NSButton class]], @"Constraint must be for NSButton object"); NSLayoutConstraint *animatedConstraint = animation ? constraint.animator : constraint; animatedConstraint.constant = 0; } - (void)showButtonWithConstraint:(NSLayoutConstraint *)constraint animation:(BOOL)animation { NSAssert([constraint.firstItem isKindOfClass:[NSButton class]], @"Constraint must be for NSButton object"); NSLayoutConstraint *animatedConstraint = animation ? constraint.animator : constraint; animatedConstraint.constant = ((NSButton *)constraint.firstItem).image.size.width; } - (void)setupEffectsButton:(BOOL)withAnimation { if (var_InheritBool(getIntf(), "macosx-show-effects-button")) [self addEffectsButton:withAnimation]; else [self removeEffectsButton:withAnimation]; } - (void)addEffectsButton:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self showButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation]; id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton; if (!self.nativeFullscreenMode) { if (self.darkInterface) { [button setImage: imageFromRes(@"fullscreen-double-buttons_dark")]; [button setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed_dark")]; } else { [button setImage: imageFromRes(@"fullscreen-double-buttons")]; [button setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")]; } } [NSAnimationContext endGrouping]; } - (void)removeEffectsButton:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self hideButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation]; id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton; if (!self.nativeFullscreenMode) { if (self.darkInterface) { [button setImage: imageFromRes(@"fullscreen-one-button_dark")]; [button setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed_dark")]; } else { [button setImage: imageFromRes(@"fullscreen-one-button")]; [button setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")]; } } [NSAnimationContext endGrouping]; } - (void)setupJumpButtons:(BOOL)withAnimation { if (var_InheritBool(getIntf(), "macosx-show-playback-buttons")) [self addJumpButtons:withAnimation]; else [self removeJumpButtons:withAnimation]; } - (void)addJumpButtons:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self showButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation]; [self showButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation]; id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton; id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton; if (self.darkInterface) { [forwardButton setImage:imageFromRes(@"forward-6btns-dark")]; [forwardButton setAlternateImage:imageFromRes(@"forward-6btns-dark-pressed")]; [backwardButton setImage:imageFromRes(@"backward-6btns-dark")]; [backwardButton setAlternateImage:imageFromRes(@"backward-6btns-dark-pressed")]; } else { [forwardButton setImage:imageFromRes(@"forward-6btns")]; [forwardButton setAlternateImage:imageFromRes(@"forward-6btns-pressed")]; [backwardButton setImage:imageFromRes(@"backward-6btns")]; [backwardButton setAlternateImage:imageFromRes(@"backward-6btns-pressed")]; } [NSAnimationContext endGrouping]; [self toggleForwardBackwardMode: YES]; } - (void)removeJumpButtons:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self hideButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation]; [self hideButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation]; id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton; id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton; if (self.darkInterface) { [forwardButton setImage:imageFromRes(@"forward-3btns-dark")]; [forwardButton setAlternateImage:imageFromRes(@"forward-3btns-dark-pressed")]; [backwardButton setImage:imageFromRes(@"backward-3btns-dark")]; [backwardButton setAlternateImage:imageFromRes(@"backward-3btns-dark-pressed")]; } else { [forwardButton setImage:imageFromRes(@"forward-3btns")]; [forwardButton setAlternateImage:imageFromRes(@"forward-3btns-pressed")]; [backwardButton setImage:imageFromRes(@"backward-3btns")]; [backwardButton setAlternateImage:imageFromRes(@"backward-3btns-pressed")]; } [NSAnimationContext endGrouping]; [self toggleForwardBackwardMode: NO]; } - (void)setupPlaymodeButtons:(BOOL)withAnimation { if (var_InheritBool(getIntf(), "macosx-show-playmode-buttons")) [self addPlaymodeButtons:withAnimation]; else [self removePlaymodeButtons:withAnimation]; } - (void)addPlaymodeButtons:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self showButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation]; [self showButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation]; id button = withAnimation ? self.playlistButton.animator : self.playlistButton; if (self.darkInterface) { [button setImage:imageFromRes(@"playlist_dark")]; [button setAlternateImage:imageFromRes(@"playlist-pressed_dark")]; } else { [button setImage:imageFromRes(@"playlist-btn")]; [button setAlternateImage:imageFromRes(@"playlist-btn-pressed")]; } [NSAnimationContext endGrouping]; } - (void)removePlaymodeButtons:(BOOL)withAnimation { [NSAnimationContext beginGrouping]; [self hideButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation]; [self hideButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation]; id button = withAnimation ? self.playlistButton.animator : self.playlistButton; if (self.darkInterface) { [button setImage:imageFromRes(@"playlist-1btn-dark")]; [button setAlternateImage:imageFromRes(@"playlist-1btn-dark-pressed")]; } else { [button setImage:imageFromRes(@"playlist-1btn")]; [button setAlternateImage:imageFromRes(@"playlist-1btn-pressed")]; } [NSAnimationContext endGrouping]; } #pragma mark - #pragma mark Extra button actions - (IBAction)stop:(id)sender { [[VLCCoreInteraction sharedInstance] stop]; } // dynamically created next / prev buttons - (IBAction)prev:(id)sender { [[VLCCoreInteraction sharedInstance] previous]; } - (IBAction)next:(id)sender { [[VLCCoreInteraction sharedInstance] next]; } - (void)setRepeatOne { [self.repeatButton setImage: _repeatOneImage]; [self.repeatButton setAlternateImage: _pressedRepeatOneImage]; _repeatOne = YES; _repeatAll = NO; } - (void)setRepeatAll { [self.repeatButton setImage: _repeatAllImage]; [self.repeatButton setAlternateImage: _pressedRepeatAllImage]; _repeatOne = NO; _repeatAll = YES; } - (void)setRepeatOff { [self.repeatButton setImage: _repeatImage]; [self.repeatButton setAlternateImage: _pressedRepeatImage]; _repeatOne = NO; _repeatAll = NO; } - (IBAction)repeat:(id)sender { vlc_value_t looping,repeating; intf_thread_t * p_intf = getIntf(); playlist_t * p_playlist = pl_Get(p_intf); var_Get(p_playlist, "repeat", &repeating); var_Get(p_playlist, "loop", &looping); if (!repeating.b_bool && !looping.b_bool) { /* was: no repeating at all, switching to Repeat One */ [[VLCCoreInteraction sharedInstance] repeatOne]; [self setRepeatOne]; } else if (repeating.b_bool && !looping.b_bool) { /* was: Repeat One, switching to Repeat All */ [[VLCCoreInteraction sharedInstance] repeatAll]; [self setRepeatAll]; } else { /* was: Repeat All or bug in VLC, switching to Repeat Off */ [[VLCCoreInteraction sharedInstance] repeatOff]; [self setRepeatOff]; } } - (void)setShuffle { bool b_value; playlist_t *p_playlist = pl_Get(getIntf()); b_value = var_GetBool(p_playlist, "random"); if (b_value) { _shuffle = YES; [self.shuffleButton setImage: _shuffleOnImage]; [self.shuffleButton setAlternateImage: _pressedShuffleOnImage]; } else { _shuffle = NO; [self.shuffleButton setImage: _shuffleImage]; [self.shuffleButton setAlternateImage: _pressedShuffleImage]; } } - (IBAction)shuffle:(id)sender { [[VLCCoreInteraction sharedInstance] shuffle]; [self setShuffle]; } - (IBAction)togglePlaylist:(id)sender { [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent]; } - (IBAction)volumeAction:(id)sender { if (sender == self.volumeSlider) [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]]; else if (sender == self.volumeDownButton) [[VLCCoreInteraction sharedInstance] toggleMute]; else [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX]; } - (IBAction)effects:(id)sender { [[[VLCMain sharedInstance] mainMenu] showAudioEffects: sender]; } #pragma mark - #pragma mark Extra updaters - (void)updateVolumeSlider { int i_volume = [[VLCCoreInteraction sharedInstance] volume]; BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute]; if (b_muted) i_volume = 0; [self.volumeSlider setIntValue: i_volume]; i_volume = (i_volume * 200) / AOUT_VOLUME_MAX; NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), i_volume]; [self.volumeSlider setToolTip:volumeTooltip]; [self.volumeSlider setEnabled: !b_muted]; [self.volumeUpButton setEnabled: !b_muted]; } - (void)updateControls { [super updateControls]; bool b_input = false; bool b_seekable = false; bool b_plmul = false; bool b_control = false; bool b_chapters = false; playlist_t * p_playlist = pl_Get(getIntf()); PL_LOCK; b_plmul = playlist_CurrentSize(p_playlist) > 1; PL_UNLOCK; input_thread_t * p_input = playlist_CurrentInput(p_playlist); if ((b_input = (p_input != NULL))) { /* seekable streams */ b_seekable = var_GetBool(p_input, "can-seek"); /* check whether slow/fast motion is possible */ b_control = var_GetBool(p_input, "can-rate"); /* chapters & titles */ //FIXME! b_chapters = p_input->stream.i_area_nb > 1; vlc_object_release(p_input); } [self.stopButton setEnabled: b_input]; [self.prevButton setEnabled: (b_seekable || b_plmul || b_chapters)]; [self.nextButton setEnabled: (b_seekable || b_plmul || b_chapters)]; [[[VLCMain sharedInstance] mainMenu] setRateControlsEnabled: b_control]; } @end