1 /*****************************************************************************
2 * ControlsBar.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2012-2013 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 "ControlsBar.h"
27 #import "CoreInteraction.h"
31 /*****************************************************************************
32 * VLCControlsBarCommon
34 * Holds all outlets, actions and code common for controls bar in detached
36 *****************************************************************************/
38 @implementation VLCControlsBarCommon
40 @synthesize bottomBarView=o_bottombar_view;
44 b_dark_interface = config_GetInt(VLCIntf, "macosx-interfacestyle");
46 b_nativeFullscreenMode = NO;
47 #ifdef MAC_OS_X_VERSION_10_7
48 if (!OSX_SNOW_LEOPARD)
49 b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
52 [o_play_btn setToolTip: _NS("Play/Pause")];
53 [[o_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
54 [[o_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
56 [o_bwd_btn setToolTip: _NS("Backward")];
57 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
58 [[o_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
60 [o_fwd_btn setToolTip: _NS("Forward")];
61 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
62 [[o_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
64 [o_time_sld setToolTip: _NS("Position")];
65 [[o_time_sld cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change current playback position.") forAttribute:NSAccessibilityDescriptionAttribute];
66 [[o_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
68 [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
69 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
70 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
72 if (!b_dark_interface) {
73 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
75 [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns"]];
76 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-pressed"]];
77 o_play_img = [[NSImage imageNamed:@"play"] retain];
78 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
79 o_pause_img = [[NSImage imageNamed:@"pause"] retain];
80 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
81 [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns"]];
82 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-pressed"]];
84 [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
85 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
87 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
88 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
90 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
92 [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns-dark"]];
93 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-dark-pressed"]];
94 o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
95 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
96 o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
97 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
98 [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
99 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
101 [o_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left_dark"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"] right: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
102 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progressbar-fill-left_dark"] middle:[NSImage imageNamed:@"progressbar-fill-middle_dark"] right:[NSImage imageNamed:@"progressbar-fill-right_dark"]];
104 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
105 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
108 [o_play_btn setImage: o_play_img];
109 [o_play_btn setAlternateImage: o_play_pressed_img];
111 NSColor *o_string_color;
112 if (!var_InheritBool(VLCIntf, "macosx-interfacestyle"))
113 o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
115 o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
116 [o_time_fld setTextColor: o_string_color];
117 [o_time_fld setFont:[NSFont titleBarFontOfSize:10.0]];
118 [o_time_fld setAlignment: NSCenterTextAlignment];
119 [o_time_fld setNeedsDisplay:YES];
120 [o_time_fld setRemainingIdentifier:@"DisplayTimeAsTimeRemaining"];
122 // prepare time slider fance gradient view
123 if (!b_dark_interface) {
125 frame = [o_time_sld_fancygradient_view frame];
126 frame.size.height = frame.size.height - 1;
127 frame.origin.y = frame.origin.y + 1;
128 [o_time_sld_fancygradient_view setFrame: frame];
132 frame = [o_time_sld_fancygradient_view frame];
133 frame.size.width = 0;
134 [o_time_sld_fancygradient_view setFrame: frame];
136 // hide resize view if necessary
137 if (!OSX_SNOW_LEOPARD)
138 [o_resize_view setImage: NULL];
140 if ([[o_bottombar_view window] styleMask] & NSResizableWindowMask)
141 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
144 // remove fullscreen button for lion fullscreen
145 if (b_nativeFullscreenMode) {
146 float f_width = [o_fullscreen_btn frame].size.width;
148 NSRect frame = [o_time_fld frame];
149 frame.origin.x += f_width;
150 [o_time_fld setFrame: frame];
152 frame = [o_progress_view frame];
153 frame.size.width = f_width + frame.size.width;
154 [o_progress_view setFrame: frame];
156 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
163 return [o_bottombar_view frame].size.height;
167 #pragma mark Button Actions
169 - (IBAction)play:(id)sender
171 [[VLCCoreInteraction sharedInstance] playOrPause];
174 - (void)resetPreviousButton
176 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
177 // seems like no further event occurred, so let's switch the playback item
178 [[VLCCoreInteraction sharedInstance] previous];
179 just_triggered_previous = NO;
183 - (void)resetBackwardSkip
185 // the user stopped skipping, so let's allow him to change the item
186 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
187 just_triggered_previous = NO;
190 - (IBAction)bwd:(id)sender
192 if (!just_triggered_previous) {
193 just_triggered_previous = YES;
194 [self performSelector:@selector(resetPreviousButton)
198 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
199 // we just skipped 4 "continous" events, otherwise we are too fast
200 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
201 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
202 [self performSelector:@selector(resetBackwardSkip)
209 - (void)resetNextButton
211 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
212 // seems like no further event occurred, so let's switch the playback item
213 [[VLCCoreInteraction sharedInstance] next];
214 just_triggered_next = NO;
218 - (void)resetForwardSkip
220 // the user stopped skipping, so let's allow him to change the item
221 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
222 just_triggered_next = NO;
225 - (IBAction)fwd:(id)sender
227 if (!just_triggered_next) {
228 just_triggered_next = YES;
229 [self performSelector:@selector(resetNextButton)
233 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
234 // we just skipped 4 "continous" events, otherwise we are too fast
235 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
236 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
237 [self performSelector:@selector(resetForwardSkip)
244 - (IBAction)timeSliderAction:(id)sender
247 input_thread_t * p_input;
249 switch([[NSApp currentEvent] type]) {
251 case NSLeftMouseDown:
252 case NSLeftMouseDragged:
253 f_updated = [sender floatValue];
259 p_input = pl_CurrentInput(VLCIntf);
260 if (p_input != NULL) {
264 pos.f_float = f_updated / 10000.;
265 var_Set(p_input, "position", pos);
266 [o_time_sld setFloatValue: f_updated];
268 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
269 [o_time_fld 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(VLCIntf);
291 var_Get(p_input, "position", &pos);
292 f_updated = 10000. * pos.f_float;
293 [o_time_sld setFloatValue: f_updated];
295 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
297 mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
299 [o_time_sld setHidden: YES];
300 [o_time_sld_fancygradient_view setHidden: YES];
302 if ([o_time_sld isHidden] == YES) {
303 bool b_buffering = false;
304 input_state_e inputState = input_GetState(p_input);
305 if (inputState == INIT_S || inputState == OPENING_S)
308 [o_time_sld setHidden: b_buffering];
309 [o_time_sld_fancygradient_view setHidden: b_buffering];
312 [o_time_fld setStringValue: o_time];
313 [o_time_fld setNeedsDisplay:YES];
315 vlc_object_release(p_input);
317 [o_time_sld setFloatValue: 0.0];
318 [o_time_fld setStringValue: @"00:00"];
319 [o_time_sld setHidden: YES];
320 [o_time_sld_fancygradient_view setHidden: YES];
324 - (void)drawFancyGradientEffectForTimeSlider
326 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
327 CGFloat f_value = [o_time_sld knobPosition];
329 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
330 if (f_value != oldFrame.size.width) {
331 if ([o_time_sld_fancygradient_view isHidden])
332 [o_time_sld_fancygradient_view setHidden: NO];
333 [o_time_sld_fancygradient_view setFrame: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height)];
337 frame = [o_time_sld_fancygradient_view frame];
338 if (frame.size.width > 0) {
339 frame.size.width = 0;
340 [o_time_sld_fancygradient_view setFrame: frame];
342 [o_time_sld_fancygradient_view setHidden: YES];
347 - (void)updateControls
349 bool b_plmul = false;
350 bool b_seekable = false;
351 bool b_chapters = false;
352 bool b_buffering = false;
354 playlist_t * p_playlist = pl_Get(VLCIntf);
357 b_plmul = playlist_CurrentSize(p_playlist) > 1;
360 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
364 input_state_e inputState = input_GetState(p_input);
365 if (inputState == INIT_S || inputState == OPENING_S)
368 /* seekable streams */
369 b_seekable = var_GetBool(p_input, "can-seek");
371 /* chapters & titles */
372 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
374 vlc_object_release(p_input);
379 [o_progress_bar startAnimation:self];
380 [o_progress_bar setIndeterminate:YES];
381 [o_progress_bar setHidden:NO];
383 [o_progress_bar stopAnimation:self];
384 [o_progress_bar setHidden:YES];
387 [o_time_sld setEnabled: b_seekable];
389 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
390 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
395 [o_play_btn setImage: o_pause_img];
396 [o_play_btn setAlternateImage: o_pause_pressed_img];
397 [o_play_btn setToolTip: _NS("Pause")];
402 [o_play_btn setImage: o_play_img];
403 [o_play_btn setAlternateImage: o_play_pressed_img];
404 [o_play_btn setToolTip: _NS("Play")];
407 - (void)setFullscreenState:(BOOL)b_fullscreen
409 if (!b_nativeFullscreenMode)
410 [o_fullscreen_btn setState:b_fullscreen];
416 /*****************************************************************************
417 * VLCMainWindowControlsBar
419 * Holds all specific outlets, actions and code for the main window controls bar.
420 *****************************************************************************/
422 @interface VLCMainWindowControlsBar (Internal)
423 - (void)addJumpButtons:(BOOL)b_fast;
424 - (void)removeJumpButtons:(BOOL)b_fast;
425 - (void)addPlaymodeButtons:(BOOL)b_fast;
426 - (void)removePlaymodeButtons:(BOOL)b_fast;
429 @implementation VLCMainWindowControlsBar
433 [super awakeFromNib];
436 [o_stop_btn setToolTip: _NS("Stop")];
437 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
438 [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
440 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
441 [[o_playlist_btn cell] accessibilitySetOverrideValue:_NS("Click to switch between video output and playlist. If no video is shown in the main window, this allows you to hide the playlist.") forAttribute:NSAccessibilityDescriptionAttribute];
442 [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
444 [o_repeat_btn setToolTip: _NS("Repeat")];
445 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
446 [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
448 [o_shuffle_btn setToolTip: _NS("Shuffle")];
449 [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
450 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
452 [o_volume_sld setToolTip: _NS("Volume")];
453 [[o_volume_sld cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change the volume.") forAttribute:NSAccessibilityDescriptionAttribute];
454 [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
455 [o_volume_down_btn setToolTip: _NS("Mute")];
456 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
457 [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
458 [o_volume_up_btn setToolTip: _NS("Full Volume")];
459 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
460 [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
462 [o_effects_btn setToolTip: _NS("Effects")];
463 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
464 [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
466 if (!b_dark_interface) {
467 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
468 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
470 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
471 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
472 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
473 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
474 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
475 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
476 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
477 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
478 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
479 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
480 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
481 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
483 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
484 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
485 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
487 if (b_nativeFullscreenMode) {
488 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
489 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed"]];
491 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
492 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
495 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
496 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
498 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
499 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
501 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
502 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
503 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
504 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
505 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
506 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
507 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
508 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
509 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
510 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
511 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
512 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
514 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
515 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
516 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
518 if (b_nativeFullscreenMode) {
519 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
520 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed-dark"]];
522 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
523 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
526 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
527 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
529 [o_repeat_btn setImage: o_repeat_img];
530 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
531 [o_shuffle_btn setImage: o_shuffle_img];
532 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
534 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
535 [o_volume_sld setEnabled: b_mute];
536 [o_volume_up_btn setEnabled: b_mute];
539 // remove fullscreen button for lion fullscreen
540 if (b_nativeFullscreenMode) {
543 // == [o_fullscreen_btn frame].size.width;
544 // button is already removed!
546 #define moveItem(item) \
547 frame = [item frame]; \
548 frame.origin.x = f_width + frame.origin.x; \
549 [item setFrame: frame]
551 moveItem(o_effects_btn);
552 moveItem(o_volume_up_btn);
553 moveItem(o_volume_sld);
554 moveItem(o_volume_track_view);
555 moveItem(o_volume_down_btn);
558 // time field and progress bar are moved in super method!
562 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
563 if (b_show_jump_buttons)
564 [self addJumpButtons:YES];
566 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
567 if (!b_show_playmode_buttons)
568 [self removePlaymodeButtons:YES];
570 if (!config_GetInt(VLCIntf, "macosx-show-effects-button"))
571 [self removeEffectsButton:YES];
573 [[VLCMain sharedInstance] playbackModeUpdated];
578 #pragma mark interface customization
581 - (void)toggleEffectsButton
583 if (config_GetInt(VLCIntf, "macosx-show-effects-button"))
584 [self addEffectsButton:NO];
586 [self removeEffectsButton:NO];
589 - (void)addEffectsButton:(BOOL)b_fast
595 [o_effects_btn setHidden: NO];
597 [[o_effects_btn animator] setHidden: NO];
600 #define moveItem(item) \
601 frame = [item frame]; \
602 frame.origin.x = frame.origin.x - f_space; \
604 [item setFrame: frame]; \
606 [[item animator] setFrame: frame]
609 float f_space = [o_effects_btn frame].size.width;
610 // extra margin between button and volume up button
611 if (b_nativeFullscreenMode)
615 moveItem(o_volume_up_btn);
616 moveItem(o_volume_sld);
617 moveItem(o_volume_track_view);
618 moveItem(o_volume_down_btn);
619 moveItem(o_time_fld);
623 frame = [o_progress_view frame];
624 frame.size.width = frame.size.width - f_space;
626 [o_progress_view setFrame: frame];
628 [[o_progress_view animator] setFrame: frame];
630 if (!b_nativeFullscreenMode) {
631 if (b_dark_interface) {
632 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
633 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
635 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
636 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
640 [o_bottombar_view setNeedsDisplay:YES];
643 - (void)removeEffectsButton:(BOOL)b_fast
648 [o_effects_btn setHidden: YES];
650 #define moveItem(item) \
651 frame = [item frame]; \
652 frame.origin.x = frame.origin.x + f_space; \
654 [item setFrame: frame]; \
656 [[item animator] setFrame: frame]
659 float f_space = [o_effects_btn frame].size.width;
660 // extra margin between button and volume up button
661 if (b_nativeFullscreenMode)
664 moveItem(o_volume_up_btn);
665 moveItem(o_volume_sld);
666 moveItem(o_volume_track_view);
667 moveItem(o_volume_down_btn);
668 moveItem(o_time_fld);
672 frame = [o_progress_view frame];
673 frame.size.width = frame.size.width + f_space;
675 [o_progress_view setFrame: frame];
677 [[o_progress_view animator] setFrame: frame];
679 if (!b_nativeFullscreenMode) {
680 if (b_dark_interface) {
681 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
682 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
684 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
685 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
689 [o_bottombar_view setNeedsDisplay:YES];
692 - (void)toggleJumpButtons
694 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
696 if (b_show_jump_buttons)
697 [self addJumpButtons:NO];
699 [self removeJumpButtons:NO];
702 - (void)addJumpButtons:(BOOL)b_fast
704 NSRect preliminaryFrame = [o_bwd_btn frame];
705 BOOL b_enabled = [o_bwd_btn isEnabled];
706 preliminaryFrame.size.width = 29.;
707 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
708 [o_prev_btn setButtonType: NSMomentaryChangeButton];
709 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
710 [o_prev_btn setBordered:NO];
711 [o_prev_btn setTarget:self];
712 [o_prev_btn setAction:@selector(prev:)];
713 [o_prev_btn setToolTip: _NS("Previous")];
714 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
715 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
716 [o_prev_btn setEnabled: b_enabled];
718 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
719 [o_next_btn setButtonType: NSMomentaryChangeButton];
720 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
721 [o_next_btn setBordered:NO];
722 [o_next_btn setTarget:self];
723 [o_next_btn setAction:@selector(next:)];
724 [o_next_btn setToolTip: _NS("Next")];
725 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
726 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
727 [o_next_btn setEnabled: b_enabled];
729 if (b_dark_interface) {
730 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
731 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
732 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
733 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
735 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
736 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
737 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
738 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
741 /* change the accessibility help for the backward/forward buttons accordingly */
742 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
743 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
746 frame = [o_bwd_btn frame];
748 [o_bwd_btn setFrame:frame];
749 frame = [o_fwd_btn frame];
751 [o_fwd_btn setFrame:frame];
753 #define moveItem(item) \
754 frame = [item frame]; \
755 frame.origin.x = frame.origin.x + f_space; \
757 [item setFrame: frame]; \
759 [[item animator] setFrame: frame]
764 moveItem(o_play_btn);
767 moveItem(o_stop_btn);
768 moveItem(o_playlist_btn);
769 moveItem(o_repeat_btn);
770 moveItem(o_shuffle_btn);
773 frame = [o_progress_view frame];
774 frame.size.width = frame.size.width - f_space;
775 frame.origin.x = frame.origin.x + f_space;
777 [o_progress_view setFrame: frame];
779 [[o_progress_view animator] setFrame: frame];
781 if (b_dark_interface) {
782 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
783 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
784 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
785 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
787 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
788 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
789 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
790 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
793 preliminaryFrame.origin.x = [o_prev_btn frame].origin.x + [o_prev_btn frame].size.width + [o_bwd_btn frame].size.width + [o_play_btn frame].size.width + [o_fwd_btn frame].size.width;
794 [o_next_btn setFrame: preliminaryFrame];
796 // wait until the animation is done, if displayed
798 [o_bottombar_view addSubview:o_prev_btn];
799 [o_bottombar_view addSubview:o_next_btn];
801 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
802 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
805 [o_fwd_btn setAction:@selector(forward:)];
806 [o_bwd_btn setAction:@selector(backward:)];
809 - (void)removeJumpButtons:(BOOL)b_fast
811 if (!o_prev_btn || !o_next_btn)
815 [o_prev_btn setHidden: YES];
816 [o_next_btn setHidden: YES];
818 [[o_prev_btn animator] setHidden: YES];
819 [[o_next_btn animator] setHidden: YES];
821 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
822 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
823 [o_prev_btn release];
825 [o_next_btn release];
828 /* change the accessibility help for the backward/forward buttons accordingly */
829 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
830 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
833 frame = [o_bwd_btn frame];
835 [o_bwd_btn setFrame:frame];
836 frame = [o_fwd_btn frame];
838 [o_fwd_btn setFrame:frame];
840 #define moveItem(item) \
841 frame = [item frame]; \
842 frame.origin.x = frame.origin.x - f_space; \
844 [item setFrame: frame]; \
846 [[item animator] setFrame: frame]
851 moveItem(o_play_btn);
854 moveItem(o_stop_btn);
855 moveItem(o_playlist_btn);
856 moveItem(o_repeat_btn);
857 moveItem(o_shuffle_btn);
860 frame = [o_progress_view frame];
861 frame.size.width = frame.size.width + f_space;
862 frame.origin.x = frame.origin.x - f_space;
864 [o_progress_view setFrame: frame];
866 [[o_progress_view animator] setFrame: frame];
868 if (b_dark_interface) {
869 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
870 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
871 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
872 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
874 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
875 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
876 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
877 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
880 [o_bottombar_view setNeedsDisplay:YES];
882 [o_fwd_btn setAction:@selector(fwd:)];
883 [o_bwd_btn setAction:@selector(bwd:)];
886 - (void)togglePlaymodeButtons
888 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
890 if (b_show_playmode_buttons)
891 [self addPlaymodeButtons:NO];
893 [self removePlaymodeButtons:NO];
896 - (void)addPlaymodeButtons:(BOOL)b_fast
899 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
901 if (b_dark_interface) {
902 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
903 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
905 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
906 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
908 frame = [o_playlist_btn frame];
910 [o_playlist_btn setFrame:frame];
913 [o_repeat_btn setHidden: NO];
914 [o_shuffle_btn setHidden: NO];
916 [[o_repeat_btn animator] setHidden: NO];
917 [[o_shuffle_btn animator] setHidden: NO];
920 frame = [o_progress_view frame];
921 frame.size.width = frame.size.width - f_space;
922 frame.origin.x = frame.origin.x + f_space;
924 [o_progress_view setFrame: frame];
926 [[o_progress_view animator] setFrame: frame];
929 - (void)removePlaymodeButtons:(BOOL)b_fast
932 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
933 [o_repeat_btn setHidden: YES];
934 [o_shuffle_btn setHidden: YES];
936 if (b_dark_interface) {
937 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
938 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
940 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
941 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
943 frame = [o_playlist_btn frame];
945 [o_playlist_btn setFrame:frame];
947 frame = [o_progress_view frame];
948 frame.size.width = frame.size.width + f_space;
949 frame.origin.x = frame.origin.x - f_space;
951 [o_progress_view setFrame: frame];
953 [[o_progress_view animator] setFrame: frame];
957 #pragma mark Extra button actions
959 - (IBAction)stop:(id)sender
961 [[VLCCoreInteraction sharedInstance] stop];
964 // dynamically created next / prev buttons
965 - (IBAction)prev:(id)sender
967 [[VLCCoreInteraction sharedInstance] previous];
970 - (IBAction)next:(id)sender
972 [[VLCCoreInteraction sharedInstance] next];
975 // alternative actions for forward / backward buttons when next / prev are activated
976 - (IBAction)forward:(id)sender
978 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
981 - (IBAction)backward:(id)sender
983 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
988 [o_repeat_btn setImage: o_repeat_one_img];
989 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
994 [o_repeat_btn setImage: o_repeat_all_img];
995 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1000 [o_repeat_btn setImage: o_repeat_img];
1001 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1004 - (IBAction)repeat:(id)sender
1006 vlc_value_t looping,repeating;
1007 intf_thread_t * p_intf = VLCIntf;
1008 playlist_t * p_playlist = pl_Get(p_intf);
1010 var_Get(p_playlist, "repeat", &repeating);
1011 var_Get(p_playlist, "loop", &looping);
1013 if (!repeating.b_bool && !looping.b_bool) {
1014 /* was: no repeating at all, switching to Repeat One */
1015 [[VLCCoreInteraction sharedInstance] repeatOne];
1016 [self setRepeatOne];
1018 else if (repeating.b_bool && !looping.b_bool) {
1019 /* was: Repeat One, switching to Repeat All */
1020 [[VLCCoreInteraction sharedInstance] repeatAll];
1021 [self setRepeatAll];
1023 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1024 [[VLCCoreInteraction sharedInstance] repeatOff];
1025 [self setRepeatOff];
1032 playlist_t *p_playlist = pl_Get(VLCIntf);
1033 b_value = var_GetBool(p_playlist, "random");
1036 [o_shuffle_btn setImage: o_shuffle_on_img];
1037 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1039 [o_shuffle_btn setImage: o_shuffle_img];
1040 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1044 - (IBAction)shuffle:(id)sender
1046 [[VLCCoreInteraction sharedInstance] shuffle];
1050 - (IBAction)volumeAction:(id)sender
1052 if (sender == o_volume_sld)
1053 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1054 else if (sender == o_volume_down_btn)
1055 [[VLCCoreInteraction sharedInstance] toggleMute];
1057 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1060 - (IBAction)effects:(id)sender
1062 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1066 #pragma mark Extra updaters
1068 - (void)updateVolumeSlider
1070 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1071 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1074 [o_volume_sld setIntValue: i_volume];
1076 [o_volume_sld setIntValue: 0];
1078 [o_volume_sld setEnabled: !b_muted];
1079 [o_volume_up_btn setEnabled: !b_muted];
1082 - (void)updateControls
1084 [super updateControls];
1086 bool b_input = false;
1087 bool b_seekable = false;
1088 bool b_plmul = false;
1089 bool b_control = false;
1090 bool b_chapters = false;
1092 playlist_t * p_playlist = pl_Get(VLCIntf);
1095 b_plmul = playlist_CurrentSize(p_playlist) > 1;
1098 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
1099 if ((b_input = (p_input != NULL))) {
1100 /* seekable streams */
1101 b_seekable = var_GetBool(p_input, "can-seek");
1103 /* check whether slow/fast motion is possible */
1104 b_control = var_GetBool(p_input, "can-rate");
1106 /* chapters & titles */
1107 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1109 vlc_object_release(p_input);
1112 [o_stop_btn setEnabled: b_input];
1114 if (b_show_jump_buttons) {
1115 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1116 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1119 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];