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 [o_time_fld setAlignment: NSCenterTextAlignment];
112 [o_time_fld setNeedsDisplay:YES];
114 // prepare time slider fance gradient view
115 if (!b_dark_interface) {
117 frame = [o_time_sld_fancygradient_view frame];
118 frame.size.height = frame.size.height - 1;
119 frame.origin.y = frame.origin.y + 1;
120 [o_time_sld_fancygradient_view setFrame: frame];
124 frame = [o_time_sld_fancygradient_view frame];
125 frame.size.width = 0;
126 [o_time_sld_fancygradient_view setFrame: frame];
128 // hide resize view if necessary
129 if (!OSX_SNOW_LEOPARD)
130 [o_resize_view setImage: NULL];
132 if ([[o_bottombar_view window] styleMask] & NSResizableWindowMask)
133 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
136 // remove fullscreen button for lion fullscreen
137 if (b_nativeFullscreenMode) {
138 float f_width = [o_fullscreen_btn frame].size.width;
140 NSRect frame = [o_time_fld frame];
141 frame.origin.x += f_width;
142 [o_time_fld setFrame: frame];
144 frame = [o_progress_view frame];
145 frame.size.width = f_width + frame.size.width;
146 [o_progress_view setFrame: frame];
148 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
154 #pragma mark Button Actions
156 - (IBAction)play:(id)sender
158 [[VLCCoreInteraction sharedInstance] play];
161 - (void)resetPreviousButton
163 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
164 // seems like no further event occurred, so let's switch the playback item
165 [[VLCCoreInteraction sharedInstance] previous];
166 just_triggered_previous = NO;
170 - (void)resetBackwardSkip
172 // the user stopped skipping, so let's allow him to change the item
173 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
174 just_triggered_previous = NO;
177 - (IBAction)bwd:(id)sender
179 if (!just_triggered_previous) {
180 just_triggered_previous = YES;
181 [self performSelector:@selector(resetPreviousButton)
185 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
186 // we just skipped 4 "continous" events, otherwise we are too fast
187 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
188 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
189 [self performSelector:@selector(resetBackwardSkip)
196 - (void)resetNextButton
198 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
199 // seems like no further event occurred, so let's switch the playback item
200 [[VLCCoreInteraction sharedInstance] next];
201 just_triggered_next = NO;
205 - (void)resetForwardSkip
207 // the user stopped skipping, so let's allow him to change the item
208 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
209 just_triggered_next = NO;
212 - (IBAction)fwd:(id)sender
214 if (!just_triggered_next) {
215 just_triggered_next = YES;
216 [self performSelector:@selector(resetNextButton)
220 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
221 // we just skipped 4 "continous" events, otherwise we are too fast
222 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
223 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
224 [self performSelector:@selector(resetForwardSkip)
231 - (IBAction)timeSliderAction:(id)sender
234 input_thread_t * p_input;
236 switch([[NSApp currentEvent] type]) {
238 case NSLeftMouseDown:
239 case NSLeftMouseDragged:
240 f_updated = [sender floatValue];
246 p_input = pl_CurrentInput(VLCIntf);
247 if (p_input != NULL) {
251 pos.f_float = f_updated / 10000.;
252 var_Set(p_input, "position", pos);
253 [o_time_sld setFloatValue: f_updated];
255 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
256 [o_time_fld setStringValue: o_time];
257 vlc_object_release(p_input);
261 - (IBAction)fullscreen:(id)sender
263 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
267 #pragma mark Updaters
269 - (void)updateTimeSlider
271 input_thread_t * p_input;
272 p_input = pl_CurrentInput(VLCIntf);
278 var_Get(p_input, "position", &pos);
279 f_updated = 10000. * pos.f_float;
280 [o_time_sld setFloatValue: f_updated];
282 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
284 mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
286 [o_time_sld setEnabled: NO];
287 [o_time_sld setHidden: YES];
288 [o_time_sld_fancygradient_view setHidden: YES];
290 [o_time_sld setEnabled: YES];
291 [o_time_sld setHidden: NO];
292 [o_time_sld_fancygradient_view setHidden: NO];
295 [o_time_fld setStringValue: o_time];
296 [o_time_fld setNeedsDisplay:YES];
297 vlc_object_release(p_input);
299 [o_time_sld setFloatValue: 0.0];
300 [o_time_fld setStringValue: @"00:00"];
301 [o_time_sld setEnabled: NO];
302 [o_time_sld setHidden: YES];
303 [o_time_sld_fancygradient_view setHidden: YES];
307 - (void)drawFancyGradientEffectForTimeSlider
309 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
310 CGFloat f_value = [o_time_sld knobPosition];
312 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
313 if (f_value != oldFrame.size.width) {
314 if ([o_time_sld_fancygradient_view isHidden])
315 [o_time_sld_fancygradient_view setHidden: NO];
316 [o_time_sld_fancygradient_view setFrame: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height)];
320 frame = [o_time_sld_fancygradient_view frame];
321 if (frame.size.width > 0) {
322 frame.size.width = 0;
323 [o_time_sld_fancygradient_view setFrame: frame];
325 [o_time_sld_fancygradient_view setHidden: YES];
330 - (void)updateControls
332 bool b_plmul = false;
333 bool b_seekable = false;
334 bool b_chapters = false;
336 playlist_t * p_playlist = pl_Get(VLCIntf);
339 b_plmul = playlist_CurrentSize(p_playlist) > 1;
342 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
346 /* seekable streams */
347 b_seekable = var_GetBool(p_input, "can-seek");
349 /* chapters & titles */
350 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
352 vlc_object_release(p_input);
355 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
356 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
361 [o_play_btn setImage: o_pause_img];
362 [o_play_btn setAlternateImage: o_pause_pressed_img];
363 [o_play_btn setToolTip: _NS("Pause")];
368 [o_play_btn setImage: o_play_img];
369 [o_play_btn setAlternateImage: o_play_pressed_img];
370 [o_play_btn setToolTip: _NS("Play")];
373 - (void)setFullscreenState:(BOOL)b_fullscreen
375 if (!b_nativeFullscreenMode)
376 [o_fullscreen_btn setState:b_fullscreen];
382 /*****************************************************************************
383 * VLCMainWindowControlsBar
385 * Holds all specific outlets, actions and code for the main window controls bar.
386 *****************************************************************************/
388 @interface VLCMainWindowControlsBar (Internal)
389 - (void)addJumpButtons:(BOOL)b_fast;
390 - (void)removeJumpButtons:(BOOL)b_fast;
391 - (void)addPlaymodeButtons:(BOOL)b_fast;
392 - (void)removePlaymodeButtons:(BOOL)b_fast;
395 @implementation VLCMainWindowControlsBar
399 [super awakeFromNib];
402 [o_stop_btn setToolTip: _NS("Stop")];
403 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
404 [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
406 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
407 [[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];
408 [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
410 [o_repeat_btn setToolTip: _NS("Repeat")];
411 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
412 [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
414 [o_shuffle_btn setToolTip: _NS("Shuffle")];
415 [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
416 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
418 [o_volume_sld setToolTip: _NS("Volume")];
419 [[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];
420 [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
421 [o_volume_down_btn setToolTip: _NS("Mute")];
422 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
423 [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
424 [o_volume_up_btn setToolTip: _NS("Full Volume")];
425 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
426 [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
428 [o_effects_btn setToolTip: _NS("Effects")];
429 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
430 [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
432 if (!b_dark_interface) {
433 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
434 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
436 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
437 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
438 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
439 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
440 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
441 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
442 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
443 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
444 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
445 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
446 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
447 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
449 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
450 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
451 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
453 if (b_nativeFullscreenMode) {
454 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
455 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed"]];
457 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
458 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
461 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
462 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
464 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
465 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
467 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
468 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
469 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
470 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
471 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
472 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
473 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
474 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
475 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
476 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
477 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
478 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
480 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
481 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
482 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
484 if (b_nativeFullscreenMode) {
485 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
486 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed-dark"]];
488 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
489 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
492 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
493 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
495 [o_repeat_btn setImage: o_repeat_img];
496 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
497 [o_shuffle_btn setImage: o_shuffle_img];
498 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
500 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
501 [o_volume_sld setEnabled: b_mute];
502 [o_volume_up_btn setEnabled: b_mute];
505 // remove fullscreen button for lion fullscreen
506 if (b_nativeFullscreenMode) {
509 // == [o_fullscreen_btn frame].size.width;
510 // button is already removed!
512 #define moveItem(item) \
513 frame = [item frame]; \
514 frame.origin.x = f_width + frame.origin.x; \
515 [item setFrame: frame]
517 moveItem(o_effects_btn);
518 moveItem(o_volume_up_btn);
519 moveItem(o_volume_sld);
520 moveItem(o_volume_track_view);
521 moveItem(o_volume_down_btn);
524 // time field and progress bar are moved in super method!
528 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
529 if (b_show_jump_buttons)
530 [self addJumpButtons:YES];
532 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
533 if (!b_show_playmode_buttons)
534 [self removePlaymodeButtons:YES];
536 [[VLCMain sharedInstance] playbackModeUpdated];
541 #pragma mark interface customization
543 - (void)toggleJumpButtons
545 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
547 if (b_show_jump_buttons)
548 [self addJumpButtons:NO];
550 [self removeJumpButtons:NO];
553 - (void)addJumpButtons:(BOOL)b_fast
555 NSRect preliminaryFrame = [o_bwd_btn frame];
556 BOOL b_enabled = [o_bwd_btn isEnabled];
557 preliminaryFrame.size.width = 29.;
558 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
559 [o_prev_btn setButtonType: NSMomentaryChangeButton];
560 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
561 [o_prev_btn setBordered:NO];
562 [o_prev_btn setTarget:self];
563 [o_prev_btn setAction:@selector(prev:)];
564 [o_prev_btn setToolTip: _NS("Previous")];
565 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
566 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
567 [o_prev_btn setEnabled: b_enabled];
569 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
570 [o_next_btn setButtonType: NSMomentaryChangeButton];
571 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
572 [o_next_btn setBordered:NO];
573 [o_next_btn setTarget:self];
574 [o_next_btn setAction:@selector(next:)];
575 [o_next_btn setToolTip: _NS("Next")];
576 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
577 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
578 [o_next_btn setEnabled: b_enabled];
580 if (b_dark_interface) {
581 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
582 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
583 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
584 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
586 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
587 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
588 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
589 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
592 /* change the accessibility help for the backward/forward buttons accordingly */
593 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
594 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
597 frame = [o_bwd_btn frame];
599 [o_bwd_btn setFrame:frame];
600 frame = [o_fwd_btn frame];
602 [o_fwd_btn setFrame:frame];
604 #define moveItem(item) \
605 frame = [item frame]; \
606 frame.origin.x = frame.origin.x + f_space; \
608 [item setFrame: frame]; \
610 [[item animator] setFrame: frame]
615 moveItem(o_play_btn);
618 moveItem(o_stop_btn);
619 moveItem(o_playlist_btn);
620 moveItem(o_repeat_btn);
621 moveItem(o_shuffle_btn);
624 frame = [o_progress_view frame];
625 frame.size.width = frame.size.width - f_space;
626 frame.origin.x = frame.origin.x + f_space;
628 [o_progress_view setFrame: frame];
630 [[o_progress_view animator] setFrame: frame];
632 if (b_dark_interface) {
633 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
634 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
635 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
636 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
638 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
639 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
640 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
641 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
644 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;
645 [o_next_btn setFrame: preliminaryFrame];
647 // wait until the animation is done, if displayed
649 [o_bottombar_view addSubview:o_prev_btn];
650 [o_bottombar_view addSubview:o_next_btn];
652 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
653 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
656 [o_fwd_btn setAction:@selector(forward:)];
657 [o_bwd_btn setAction:@selector(backward:)];
660 - (void)removeJumpButtons:(BOOL)b_fast
662 if (!o_prev_btn || !o_next_btn)
666 [o_prev_btn setHidden: YES];
667 [o_next_btn setHidden: YES];
669 [[o_prev_btn animator] setHidden: YES];
670 [[o_next_btn animator] setHidden: YES];
672 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
673 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
674 [o_prev_btn release];
676 [o_next_btn release];
679 /* change the accessibility help for the backward/forward buttons accordingly */
680 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
681 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
684 frame = [o_bwd_btn frame];
686 [o_bwd_btn setFrame:frame];
687 frame = [o_fwd_btn frame];
689 [o_fwd_btn setFrame:frame];
691 #define moveItem(item) \
692 frame = [item frame]; \
693 frame.origin.x = frame.origin.x - f_space; \
695 [item setFrame: frame]; \
697 [[item animator] setFrame: frame]
702 moveItem(o_play_btn);
705 moveItem(o_stop_btn);
706 moveItem(o_playlist_btn);
707 moveItem(o_repeat_btn);
708 moveItem(o_shuffle_btn);
711 frame = [o_progress_view frame];
712 frame.size.width = frame.size.width + f_space;
713 frame.origin.x = frame.origin.x - f_space;
715 [o_progress_view setFrame: frame];
717 [[o_progress_view animator] setFrame: frame];
719 if (b_dark_interface) {
720 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
721 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
722 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
723 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
725 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
726 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
727 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
728 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
731 [o_bottombar_view setNeedsDisplay:YES];
733 [o_fwd_btn setAction:@selector(fwd:)];
734 [o_bwd_btn setAction:@selector(bwd:)];
737 - (void)togglePlaymodeButtons
739 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
741 if (b_show_playmode_buttons)
742 [self addPlaymodeButtons:NO];
744 [self removePlaymodeButtons:NO];
747 - (void)addPlaymodeButtons:(BOOL)b_fast
750 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
752 if (b_dark_interface) {
753 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
754 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
756 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
757 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
759 frame = [o_playlist_btn frame];
761 [o_playlist_btn setFrame:frame];
764 [o_repeat_btn setHidden: NO];
765 [o_shuffle_btn setHidden: NO];
767 [[o_repeat_btn animator] setHidden: NO];
768 [[o_shuffle_btn animator] setHidden: NO];
771 frame = [o_progress_view frame];
772 frame.size.width = frame.size.width - f_space;
773 frame.origin.x = frame.origin.x + f_space;
775 [o_progress_view setFrame: frame];
777 [[o_progress_view animator] setFrame: frame];
780 - (void)removePlaymodeButtons:(BOOL)b_fast
783 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
784 [o_repeat_btn setHidden: YES];
785 [o_shuffle_btn setHidden: YES];
787 if (b_dark_interface) {
788 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
789 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
791 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
792 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
794 frame = [o_playlist_btn frame];
796 [o_playlist_btn setFrame:frame];
798 frame = [o_progress_view frame];
799 frame.size.width = frame.size.width + f_space;
800 frame.origin.x = frame.origin.x - f_space;
802 [o_progress_view setFrame: frame];
804 [[o_progress_view animator] setFrame: frame];
808 #pragma mark Extra button actions
810 - (IBAction)stop:(id)sender
812 [[VLCCoreInteraction sharedInstance] stop];
815 // dynamically created next / prev buttons
816 - (IBAction)prev:(id)sender
818 [[VLCCoreInteraction sharedInstance] previous];
821 - (IBAction)next:(id)sender
823 [[VLCCoreInteraction sharedInstance] next];
826 // alternative actions for forward / backward buttons when next / prev are activated
827 - (IBAction)forward:(id)sender
829 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
832 - (IBAction)backward:(id)sender
834 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
839 [o_repeat_btn setImage: o_repeat_one_img];
840 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
845 [o_repeat_btn setImage: o_repeat_all_img];
846 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
851 [o_repeat_btn setImage: o_repeat_img];
852 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
855 - (IBAction)repeat:(id)sender
857 vlc_value_t looping,repeating;
858 intf_thread_t * p_intf = VLCIntf;
859 playlist_t * p_playlist = pl_Get(p_intf);
861 var_Get(p_playlist, "repeat", &repeating);
862 var_Get(p_playlist, "loop", &looping);
864 if (!repeating.b_bool && !looping.b_bool) {
865 /* was: no repeating at all, switching to Repeat One */
866 [[VLCCoreInteraction sharedInstance] repeatOne];
869 else if (repeating.b_bool && !looping.b_bool) {
870 /* was: Repeat One, switching to Repeat All */
871 [[VLCCoreInteraction sharedInstance] repeatAll];
874 /* was: Repeat All or bug in VLC, switching to Repeat Off */
875 [[VLCCoreInteraction sharedInstance] repeatOff];
883 playlist_t *p_playlist = pl_Get(VLCIntf);
884 b_value = var_GetBool(p_playlist, "random");
887 [o_shuffle_btn setImage: o_shuffle_on_img];
888 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
890 [o_shuffle_btn setImage: o_shuffle_img];
891 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
895 - (IBAction)shuffle:(id)sender
897 [[VLCCoreInteraction sharedInstance] shuffle];
901 - (IBAction)volumeAction:(id)sender
903 if (sender == o_volume_sld)
904 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
905 else if (sender == o_volume_down_btn)
906 [[VLCCoreInteraction sharedInstance] toggleMute];
908 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
911 - (IBAction)effects:(id)sender
913 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
917 #pragma mark Extra updaters
919 - (void)updateVolumeSlider
921 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
922 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
925 [o_volume_sld setIntValue: i_volume];
927 [o_volume_sld setIntValue: 0];
929 [o_volume_sld setEnabled: !b_muted];
930 [o_volume_up_btn setEnabled: !b_muted];
933 - (void)updateControls
935 [super updateControls];
937 bool b_input = false;
938 bool b_plmul = false;
939 bool b_control = false;
940 bool b_seekable = false;
941 bool b_chapters = false;
943 playlist_t * p_playlist = pl_Get(VLCIntf);
946 b_plmul = playlist_CurrentSize(p_playlist) > 1;
949 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
951 bool b_buffering = NO;
953 if ((b_input = (p_input != NULL))) {
954 /* seekable streams */
955 input_state_e inputState = input_GetState(p_input);
956 if (inputState == INIT_S || inputState == OPENING_S)
959 /* seekable streams */
960 b_seekable = var_GetBool(p_input, "can-seek");
962 /* check whether slow/fast motion is possible */
963 b_control = var_GetBool(p_input, "can-rate");
965 /* chapters & titles */
966 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
968 vlc_object_release(p_input);
972 [o_progress_bar startAnimation:self];
973 [o_progress_bar setIndeterminate:YES];
974 [o_progress_bar setHidden:NO];
976 [o_progress_bar stopAnimation:self];
977 [o_progress_bar setHidden:YES];
980 [o_stop_btn setEnabled: b_input];
982 if (b_show_jump_buttons) {
983 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
984 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
987 [o_time_sld setEnabled: b_seekable];
988 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
991 - (void)updatePosAndTimeInFSPanel:(VLCFSPanel *)o_fspanel
993 [o_fspanel setStreamPos:[o_time_sld floatValue] andTime: [o_time_fld stringValue]];