VLCMainWindowControlsBar.m 19.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * ControlsBar.m: MacOS X interface module
 *****************************************************************************
4
 * Copyright (C) 2012-2016 VLC authors and VideoLAN
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
 *
 * 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.
 *****************************************************************************/

25 26
#import "VLCControlsBarCommon.h"
#import "VLCMainWindowControlsBar.h"
27
#import "VLCMain.h"
28
#import "VLCCoreInteraction.h"
29
#import "VLCMainMenu.h"
30
#import "VLCPlaylist.h"
31
#import "CompatibilityFixes.h"
32 33 34 35 36 37 38

/*****************************************************************************
 * VLCMainWindowControlsBar
 *
 *  Holds all specific outlets, actions and code for the main window controls bar.
 *****************************************************************************/

39 40
@interface VLCMainWindowControlsBar()
{
41 42 43 44 45 46 47 48 49 50
    NSImage * _repeatImage;
    NSImage * _pressedRepeatImage;
    NSImage * _repeatAllImage;
    NSImage * _pressedRepeatAllImage;
    NSImage * _repeatOneImage;
    NSImage * _pressedRepeatOneImage;
    NSImage * _shuffleImage;
    NSImage * _pressedShuffleImage;
    NSImage * _shuffleOnImage;
    NSImage * _pressedShuffleOnImage;
51 52
}

53 54
@end

55 56 57 58 59 60
@implementation VLCMainWindowControlsBar

- (void)awakeFromNib
{
    [super awakeFromNib];

61
    [self.stopButton setToolTip: _NS("Stop")];
62
    [[self.stopButton cell] accessibilitySetOverrideValue:[self.stopButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute];
63

64
    [self.playlistButton setToolTip: _NS("Show/Hide Playlist")];
65
    [[self.playlistButton cell] accessibilitySetOverrideValue:[self.playlistButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute];
66

67
    [self.repeatButton setToolTip: _NS("Repeat")];
68
    [[self.repeatButton cell] accessibilitySetOverrideValue:_NS("Change repeat mode. Modes: repeat one, repeat all and no repeat.") forAttribute:NSAccessibilityDescriptionAttribute];
69
    [[self.repeatButton cell] accessibilitySetOverrideValue:[self.repeatButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
70

71
    [self.shuffleButton setToolTip: _NS("Shuffle")];
72
    [[self.shuffleButton cell] accessibilitySetOverrideValue:[self.shuffleButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute];
73

74
    NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), 100];
75
    [self.volumeSlider setToolTip: volumeTooltip];
76 77
    [[self.volumeSlider cell] accessibilitySetOverrideValue:_NS("Volume") forAttribute:NSAccessibilityDescriptionAttribute];
    
78
    [self.volumeDownButton setToolTip: _NS("Mute")];
79 80
    [[self.volumeDownButton cell] accessibilitySetOverrideValue:[self.volumeDownButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute];
    
81
    [self.volumeUpButton setToolTip: _NS("Full Volume")];
82
    [[self.volumeUpButton cell] accessibilitySetOverrideValue:[self.volumeUpButton toolTip] forAttribute:NSAccessibilityDescriptionAttribute];
83 84

    [self.effectsButton setToolTip: _NS("Audio Effects")];
85
    [[self.effectsButton cell] accessibilitySetOverrideValue:_NS("Open Audio Effects window") forAttribute:NSAccessibilityDescriptionAttribute];
86
    [[self.effectsButton cell] accessibilitySetOverrideValue:[self.effectsButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
87

88
    if (!self.darkInterface) {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
        [self.stopButton setImage: imageFromRes(@"stop")];
        [self.stopButton setAlternateImage: imageFromRes(@"stop-pressed")];

        [self.playlistButton setImage: imageFromRes(@"playlist-btn")];
        [self.playlistButton setAlternateImage: imageFromRes(@"playlist-btn-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];
108

109
        if (self.nativeFullscreenMode) {
110 111
            [self.effectsButton setImage: imageFromRes(@"effects-one-button")];
            [self.effectsButton setAlternateImage: imageFromRes(@"effects-one-button-pressed")];
112
        } else {
113 114
            [self.effectsButton setImage: imageFromRes(@"effects-double-buttons")];
            [self.effectsButton setAlternateImage: imageFromRes(@"effects-double-buttons-pressed")];
115 116
        }

117 118
        [self.fullscreenButton setImage: imageFromRes(@"fullscreen-double-buttons")];
        [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")];
119 120 121 122 123

        [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")];
124
    } else {
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        [self.stopButton setImage: imageFromRes(@"stop_dark")];
        [self.stopButton setAlternateImage: imageFromRes(@"stop-pressed_dark")];

        [self.playlistButton setImage: imageFromRes(@"playlist_dark")];
        [self.playlistButton setAlternateImage: imageFromRes(@"playlist-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];
144

145
        if (self.nativeFullscreenMode) {
146 147
            [self.effectsButton setImage: imageFromRes(@"effects-one-button_dark")];
            [self.effectsButton setAlternateImage: imageFromRes(@"effects-one-button-pressed-dark")];
148
        } else {
149 150
            [self.effectsButton setImage: imageFromRes(@"effects-double-buttons_dark")];
            [self.effectsButton setAlternateImage: imageFromRes(@"effects-double-buttons-pressed_dark")];
151 152
        }

153 154
        [self.fullscreenButton setImage: imageFromRes(@"fullscreen-double-buttons_dark")];
        [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed_dark")];
155 156 157 158 159

        [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")];
160
    }
161 162 163 164
    [self.repeatButton setImage: _repeatImage];
    [self.repeatButton setAlternateImage: _pressedRepeatImage];
    [self.shuffleButton setImage: _shuffleImage];
    [self.shuffleButton setAlternateImage: _pressedShuffleImage];
165 166

    BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
167 168
    [self.volumeSlider setEnabled: b_mute];
    [self.volumeSlider setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]];
169
    [self.volumeSlider setDefaultValue: AOUT_VOLUME_DEFAULT];
170
    [self.volumeUpButton setEnabled: b_mute];
171

172 173
    // configure optional buttons
    if (!var_InheritBool(getIntf(), "macosx-show-effects-button"))
174
        [self removeEffectsButton:NO];
175

176
    if (!var_InheritBool(getIntf(), "macosx-show-playmode-buttons"))
177
        [self removePlaymodeButtons:NO];
178

179
    if (!var_InheritBool(getIntf(), "macosx-show-playback-buttons"))
180
        [self removeJumpButtons:NO];
181

182
    [[[VLCMain sharedInstance] playlist] playbackModeUpdated];
183

184 185 186 187 188
}

#pragma mark -
#pragma mark interface customization

189

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
- (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;
}

206 207
- (void)toggleEffectsButton
{
208
    if (var_InheritBool(getIntf(), "macosx-show-effects-button"))
209
        [self addEffectsButton:YES];
210
    else
211
        [self removeEffectsButton:YES];
212 213
}

214
- (void)addEffectsButton:(BOOL)withAnimation
215
{
216 217
    [NSAnimationContext beginGrouping];
    [self showButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation];
218

219
    id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton;
220 221
    if (!self.nativeFullscreenMode) {
        if (self.darkInterface) {
222 223
            [button setImage: imageFromRes(@"fullscreen-double-buttons_dark")];
            [button setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed_dark")];
224
        } else {
225 226
            [button setImage: imageFromRes(@"fullscreen-double-buttons")];
            [button setAlternateImage: imageFromRes(@"fullscreen-double-buttons-pressed")];
227 228
        }
    }
229
    [NSAnimationContext endGrouping];
230 231
}

232
- (void)removeEffectsButton:(BOOL)withAnimation
233
{
234 235
    [NSAnimationContext beginGrouping];
    [self hideButtonWithConstraint:self.effectsButtonWidthConstraint animation:withAnimation];
236

237
    id button = withAnimation ? self.fullscreenButton.animator : self.fullscreenButton;
238 239
    if (!self.nativeFullscreenMode) {
        if (self.darkInterface) {
240 241
            [button setImage: imageFromRes(@"fullscreen-one-button_dark")];
            [button setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed_dark")];
242
        } else {
243 244
            [button setImage: imageFromRes(@"fullscreen-one-button")];
            [button setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")];
245 246
        }
    }
247
    [NSAnimationContext endGrouping];
248 249
}

250 251
- (void)toggleJumpButtons
{
252
    if (var_InheritBool(getIntf(), "macosx-show-playback-buttons"))
253
        [self addJumpButtons:YES];
254
    else
255
        [self removeJumpButtons:YES];
256 257
}

258
- (void)addJumpButtons:(BOOL)withAnimation
259
{
260 261 262
    [NSAnimationContext beginGrouping];
    [self showButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation];
    [self showButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation];
263

264 265
    id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton;
    id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton;
266
    if (self.darkInterface) {
267 268 269 270
        [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")];
271
    } else {
272 273 274 275
        [forwardButton setImage:imageFromRes(@"forward-6btns")];
        [forwardButton setAlternateImage:imageFromRes(@"forward-6btns-pressed")];
        [backwardButton setImage:imageFromRes(@"backward-6btns")];
        [backwardButton setAlternateImage:imageFromRes(@"backward-6btns-pressed")];
276
    }
277
    [NSAnimationContext endGrouping];
278

279
    [self toggleForwardBackwardMode: YES];
280 281
}

282
- (void)removeJumpButtons:(BOOL)withAnimation
283
{
284 285 286
    [NSAnimationContext beginGrouping];
    [self hideButtonWithConstraint:self.prevButtonWidthConstraint animation:withAnimation];
    [self hideButtonWithConstraint:self.nextButtonWidthConstraint animation:withAnimation];
287

288 289
    id backwardButton = withAnimation ? self.backwardButton.animator : self.backwardButton;
    id forwardButton = withAnimation ? self.forwardButton.animator : self.forwardButton;
290
    if (self.darkInterface) {
291 292 293 294
        [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")];
295
    } else {
296 297 298 299
        [forwardButton setImage:imageFromRes(@"forward-3btns")];
        [forwardButton setAlternateImage:imageFromRes(@"forward-3btns-pressed")];
        [backwardButton setImage:imageFromRes(@"backward-3btns")];
        [backwardButton setAlternateImage:imageFromRes(@"backward-3btns-pressed")];
300
    }
301
    [NSAnimationContext endGrouping];
302

303
    [self toggleForwardBackwardMode: NO];
304 305 306 307
}

- (void)togglePlaymodeButtons
{
308
    if (var_InheritBool(getIntf(), "macosx-show-playmode-buttons"))
309
        [self addPlaymodeButtons:YES];
310
    else
311
        [self removePlaymodeButtons:YES];
312 313
}

314
- (void)addPlaymodeButtons:(BOOL)withAnimation
315
{
316 317 318
    [NSAnimationContext beginGrouping];
    [self showButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation];
    [self showButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation];
319

320
    id button = withAnimation ? self.playlistButton.animator : self.playlistButton;
321
    if (self.darkInterface) {
322 323
        [button setImage:imageFromRes(@"playlist_dark")];
        [button setAlternateImage:imageFromRes(@"playlist-pressed_dark")];
324
    } else {
325 326
        [button setImage:imageFromRes(@"playlist-btn")];
        [button setAlternateImage:imageFromRes(@"playlist-btn-pressed")];
327
    }
328
    [NSAnimationContext endGrouping];
329 330
}

331
- (void)removePlaymodeButtons:(BOOL)withAnimation
332
{
333 334 335 336
    [NSAnimationContext beginGrouping];

    [self hideButtonWithConstraint:self.repeatButtonWidthConstraint animation:withAnimation];
    [self hideButtonWithConstraint:self.shuffleButtonWidthConstraint animation:withAnimation];
337

338
    id button = withAnimation ? self.playlistButton.animator : self.playlistButton;
339
    if (self.darkInterface) {
340 341
        [button setImage:imageFromRes(@"playlist-1btn-dark")];
        [button setAlternateImage:imageFromRes(@"playlist-1btn-dark-pressed")];
342
    } else {
343 344
        [button setImage:imageFromRes(@"playlist-1btn")];
        [button setAlternateImage:imageFromRes(@"playlist-1btn-pressed")];
345
    }
346
    [NSAnimationContext endGrouping];
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
}

#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
{
370 371
    [self.repeatButton setImage: _repeatOneImage];
    [self.repeatButton setAlternateImage: _pressedRepeatOneImage];
372 373 374 375
}

- (void)setRepeatAll
{
376 377
    [self.repeatButton setImage: _repeatAllImage];
    [self.repeatButton setAlternateImage: _pressedRepeatAllImage];
378 379 380 381
}

- (void)setRepeatOff
{
382 383
    [self.repeatButton setImage: _repeatImage];
    [self.repeatButton setAlternateImage: _pressedRepeatImage];
384 385 386 387 388
}

- (IBAction)repeat:(id)sender
{
    vlc_value_t looping,repeating;
389
    intf_thread_t * p_intf = getIntf();
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    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;
414
    playlist_t *p_playlist = pl_Get(getIntf());
415 416 417
    b_value = var_GetBool(p_playlist, "random");

    if (b_value) {
418 419
        [self.shuffleButton setImage: _shuffleOnImage];
        [self.shuffleButton setAlternateImage: _pressedShuffleOnImage];
420
    } else {
421 422
        [self.shuffleButton setImage: _shuffleImage];
        [self.shuffleButton setAlternateImage: _pressedShuffleImage];
423 424 425 426 427 428 429 430 431
    }
}

- (IBAction)shuffle:(id)sender
{
    [[VLCCoreInteraction sharedInstance] shuffle];
    [self setShuffle];
}

432 433 434 435 436
- (IBAction)togglePlaylist:(id)sender
{
    [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent];
}

437 438
- (IBAction)volumeAction:(id)sender
{
439
    if (sender == self.volumeSlider)
440
        [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
441
    else if (sender == self.volumeDownButton)
442 443 444 445 446 447 448
        [[VLCCoreInteraction sharedInstance] toggleMute];
    else
        [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
}

- (IBAction)effects:(id)sender
{
449
    [[[VLCMain sharedInstance] mainMenu] showAudioEffects: sender];
450 451 452 453 454 455 456 457 458 459
}

#pragma mark -
#pragma mark Extra updaters

- (void)updateVolumeSlider
{
    int i_volume = [[VLCCoreInteraction sharedInstance] volume];
    BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];

460 461 462
    if (b_muted)
        i_volume = 0;

463
    [self.volumeSlider setIntValue: i_volume];
464 465 466

    i_volume = (i_volume * 200) / AOUT_VOLUME_MAX;
    NSString *volumeTooltip = [NSString stringWithFormat:_NS("Volume: %i %%"), i_volume];
467
    [self.volumeSlider setToolTip:volumeTooltip];
468

469 470
    [self.volumeSlider setEnabled: !b_muted];
    [self.volumeUpButton setEnabled: !b_muted];
471 472 473 474 475 476 477
}

- (void)updateControls
{
    [super updateControls];

    bool b_input = false;
478
    bool b_seekable = false;
479 480 481 482
    bool b_plmul = false;
    bool b_control = false;
    bool b_chapters = false;

483
    playlist_t * p_playlist = pl_Get(getIntf());
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498

    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;
499

500 501 502
        vlc_object_release(p_input);
    }

503
    [self.stopButton setEnabled: b_input];
504 505
    [self.prevButton setEnabled: (b_seekable || b_plmul || b_chapters)];
    [self.nextButton setEnabled: (b_seekable || b_plmul || b_chapters)];
506

507
    [[[VLCMain sharedInstance] mainMenu] setRateControlsEnabled: b_control];
508 509
}

510
@end