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];
159 if (config_GetInt(VLCIntf, "macosx-show-playback-buttons"))
160 [self toggleForwardBackwardMode: YES];
166 return [o_bottombar_view frame].size.height;
169 - (void)toggleForwardBackwardMode:(BOOL)b_alt
172 /* change the accessibility help for the backward/forward buttons accordingly */
173 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
174 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
176 [o_fwd_btn setAction:@selector(alternateForward:)];
177 [o_bwd_btn setAction:@selector(alternateBackward:)];
180 /* change the accessibility help for the backward/forward buttons accordingly */
181 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
182 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
184 [o_fwd_btn setAction:@selector(fwd:)];
185 [o_bwd_btn setAction:@selector(bwd:)];
190 #pragma mark Button Actions
192 - (IBAction)play:(id)sender
194 [[VLCCoreInteraction sharedInstance] playOrPause];
197 - (void)resetPreviousButton
199 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
200 // seems like no further event occurred, so let's switch the playback item
201 [[VLCCoreInteraction sharedInstance] previous];
202 just_triggered_previous = NO;
206 - (void)resetBackwardSkip
208 // the user stopped skipping, so let's allow him to change the item
209 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
210 just_triggered_previous = NO;
213 - (IBAction)bwd:(id)sender
215 if (!just_triggered_previous) {
216 just_triggered_previous = YES;
217 [self performSelector:@selector(resetPreviousButton)
221 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
222 // we just skipped 4 "continous" events, otherwise we are too fast
223 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
224 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
225 [self performSelector:@selector(resetBackwardSkip)
232 - (void)resetNextButton
234 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
235 // seems like no further event occurred, so let's switch the playback item
236 [[VLCCoreInteraction sharedInstance] next];
237 just_triggered_next = NO;
241 - (void)resetForwardSkip
243 // the user stopped skipping, so let's allow him to change the item
244 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
245 just_triggered_next = NO;
248 - (IBAction)fwd:(id)sender
250 if (!just_triggered_next) {
251 just_triggered_next = YES;
252 [self performSelector:@selector(resetNextButton)
256 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
257 // we just skipped 4 "continous" events, otherwise we are too fast
258 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
259 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
260 [self performSelector:@selector(resetForwardSkip)
267 // alternative actions for forward / backward buttons when next / prev are activated
268 - (IBAction)alternateForward:(id)sender
270 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
273 - (IBAction)alternateBackward:(id)sender
275 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
278 - (IBAction)timeSliderAction:(id)sender
281 input_thread_t * p_input;
283 switch([[NSApp currentEvent] type]) {
285 case NSLeftMouseDown:
286 case NSLeftMouseDragged:
287 f_updated = [sender floatValue];
293 p_input = pl_CurrentInput(VLCIntf);
294 if (p_input != NULL) {
298 pos.f_float = f_updated / 10000.;
299 var_Set(p_input, "position", pos);
300 [o_time_sld setFloatValue: f_updated];
302 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
303 [o_time_fld setStringValue: o_time];
304 vlc_object_release(p_input);
308 - (IBAction)fullscreen:(id)sender
310 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
314 #pragma mark Updaters
316 - (void)updateTimeSlider
318 input_thread_t * p_input;
319 p_input = pl_CurrentInput(VLCIntf);
325 var_Get(p_input, "position", &pos);
326 f_updated = 10000. * pos.f_float;
327 [o_time_sld setFloatValue: f_updated];
329 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
331 mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
333 [o_time_sld setHidden: YES];
334 [o_time_sld_fancygradient_view setHidden: YES];
336 if ([o_time_sld isHidden] == YES) {
337 bool b_buffering = false;
338 input_state_e inputState = input_GetState(p_input);
339 if (inputState == INIT_S || inputState == OPENING_S)
342 [o_time_sld setHidden: b_buffering];
343 [o_time_sld_fancygradient_view setHidden: b_buffering];
346 [o_time_fld setStringValue: o_time];
347 [o_time_fld setNeedsDisplay:YES];
349 vlc_object_release(p_input);
351 [o_time_sld setFloatValue: 0.0];
352 [o_time_fld setStringValue: @"00:00"];
353 [o_time_sld setHidden: YES];
354 [o_time_sld_fancygradient_view setHidden: YES];
358 - (void)drawFancyGradientEffectForTimeSlider
360 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
361 CGFloat f_value = [o_time_sld knobPosition];
363 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
364 if (f_value != oldFrame.size.width) {
365 if ([o_time_sld_fancygradient_view isHidden])
366 [o_time_sld_fancygradient_view setHidden: NO];
367 [o_time_sld_fancygradient_view setFrame: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height)];
371 frame = [o_time_sld_fancygradient_view frame];
372 if (frame.size.width > 0) {
373 frame.size.width = 0;
374 [o_time_sld_fancygradient_view setFrame: frame];
376 [o_time_sld_fancygradient_view setHidden: YES];
381 - (void)updateControls
383 bool b_plmul = false;
384 bool b_seekable = false;
385 bool b_chapters = false;
386 bool b_buffering = false;
388 playlist_t * p_playlist = pl_Get(VLCIntf);
391 b_plmul = playlist_CurrentSize(p_playlist) > 1;
394 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
398 input_state_e inputState = input_GetState(p_input);
399 if (inputState == INIT_S || inputState == OPENING_S)
402 /* seekable streams */
403 b_seekable = var_GetBool(p_input, "can-seek");
405 /* chapters & titles */
406 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
408 vlc_object_release(p_input);
413 [o_progress_bar startAnimation:self];
414 [o_progress_bar setIndeterminate:YES];
415 [o_progress_bar setHidden:NO];
417 [o_progress_bar stopAnimation:self];
418 [o_progress_bar setHidden:YES];
421 [o_time_sld setEnabled: b_seekable];
423 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
424 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
429 [o_play_btn setImage: o_pause_img];
430 [o_play_btn setAlternateImage: o_pause_pressed_img];
431 [o_play_btn setToolTip: _NS("Pause")];
436 [o_play_btn setImage: o_play_img];
437 [o_play_btn setAlternateImage: o_play_pressed_img];
438 [o_play_btn setToolTip: _NS("Play")];
441 - (void)setFullscreenState:(BOOL)b_fullscreen
443 if (!b_nativeFullscreenMode)
444 [o_fullscreen_btn setState:b_fullscreen];
450 /*****************************************************************************
451 * VLCMainWindowControlsBar
453 * Holds all specific outlets, actions and code for the main window controls bar.
454 *****************************************************************************/
456 @interface VLCMainWindowControlsBar (Internal)
457 - (void)addJumpButtons:(BOOL)b_fast;
458 - (void)removeJumpButtons:(BOOL)b_fast;
459 - (void)addPlaymodeButtons:(BOOL)b_fast;
460 - (void)removePlaymodeButtons:(BOOL)b_fast;
463 @implementation VLCMainWindowControlsBar
467 [super awakeFromNib];
470 [o_stop_btn setToolTip: _NS("Stop")];
471 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
472 [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
474 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
475 [[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];
476 [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
478 [o_repeat_btn setToolTip: _NS("Repeat")];
479 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
480 [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
482 [o_shuffle_btn setToolTip: _NS("Shuffle")];
483 [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
484 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
486 [o_volume_sld setToolTip: _NS("Volume")];
487 [[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];
488 [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
489 [o_volume_down_btn setToolTip: _NS("Mute")];
490 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
491 [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
492 [o_volume_up_btn setToolTip: _NS("Full Volume")];
493 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
494 [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
496 [o_effects_btn setToolTip: _NS("Effects")];
497 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
498 [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
500 if (!b_dark_interface) {
501 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
502 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
504 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
505 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
506 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
507 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
508 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
509 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
510 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
511 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
512 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
513 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
514 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
515 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
517 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
518 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
519 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
520 [o_volume_sld setUsesBrightArtwork: YES];
522 if (b_nativeFullscreenMode) {
523 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
524 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed"]];
526 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
527 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
530 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
531 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
533 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
534 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
536 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
537 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
538 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
539 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
540 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
541 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
542 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
543 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
544 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
545 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
546 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
547 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
549 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
550 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
551 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
552 [o_volume_sld setUsesBrightArtwork: NO];
554 if (b_nativeFullscreenMode) {
555 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
556 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed-dark"]];
558 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
559 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
562 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
563 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
565 [o_repeat_btn setImage: o_repeat_img];
566 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
567 [o_shuffle_btn setImage: o_shuffle_img];
568 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
570 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
571 [o_volume_sld setEnabled: b_mute];
572 [o_volume_sld setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]];
573 [o_volume_up_btn setEnabled: b_mute];
575 // remove fullscreen button for lion fullscreen
576 if (b_nativeFullscreenMode) {
579 // == [o_fullscreen_btn frame].size.width;
580 // button is already removed!
582 #define moveItem(item) \
583 frame = [item frame]; \
584 frame.origin.x = f_width + frame.origin.x; \
585 [item setFrame: frame]
587 moveItem(o_effects_btn);
588 moveItem(o_volume_up_btn);
589 moveItem(o_volume_sld);
590 moveItem(o_volume_track_view);
591 moveItem(o_volume_down_btn);
594 // time field and progress bar are moved in super method!
598 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
599 if (b_show_jump_buttons)
600 [self addJumpButtons:YES];
602 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
603 if (!b_show_playmode_buttons)
604 [self removePlaymodeButtons:YES];
606 if (!config_GetInt(VLCIntf, "macosx-show-effects-button"))
607 [self removeEffectsButton:YES];
609 [[VLCMain sharedInstance] playbackModeUpdated];
614 #pragma mark interface customization
617 - (void)toggleEffectsButton
619 if (config_GetInt(VLCIntf, "macosx-show-effects-button"))
620 [self addEffectsButton:NO];
622 [self removeEffectsButton:NO];
625 - (void)addEffectsButton:(BOOL)b_fast
631 [o_effects_btn setHidden: NO];
633 [[o_effects_btn animator] setHidden: NO];
636 #define moveItem(item) \
637 frame = [item frame]; \
638 frame.origin.x = frame.origin.x - f_space; \
640 [item setFrame: frame]; \
642 [[item animator] setFrame: frame]
645 float f_space = [o_effects_btn frame].size.width;
646 // extra margin between button and volume up button
647 if (b_nativeFullscreenMode)
651 moveItem(o_volume_up_btn);
652 moveItem(o_volume_sld);
653 moveItem(o_volume_track_view);
654 moveItem(o_volume_down_btn);
655 moveItem(o_time_fld);
659 frame = [o_progress_view frame];
660 frame.size.width = frame.size.width - f_space;
662 [o_progress_view setFrame: frame];
664 [[o_progress_view animator] setFrame: frame];
666 if (!b_nativeFullscreenMode) {
667 if (b_dark_interface) {
668 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
669 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
671 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
672 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
676 [o_bottombar_view setNeedsDisplay:YES];
679 - (void)removeEffectsButton:(BOOL)b_fast
684 [o_effects_btn setHidden: YES];
686 #define moveItem(item) \
687 frame = [item frame]; \
688 frame.origin.x = frame.origin.x + f_space; \
690 [item setFrame: frame]; \
692 [[item animator] setFrame: frame]
695 float f_space = [o_effects_btn frame].size.width;
696 // extra margin between button and volume up button
697 if (b_nativeFullscreenMode)
700 moveItem(o_volume_up_btn);
701 moveItem(o_volume_sld);
702 moveItem(o_volume_track_view);
703 moveItem(o_volume_down_btn);
704 moveItem(o_time_fld);
708 frame = [o_progress_view frame];
709 frame.size.width = frame.size.width + f_space;
711 [o_progress_view setFrame: frame];
713 [[o_progress_view animator] setFrame: frame];
715 if (!b_nativeFullscreenMode) {
716 if (b_dark_interface) {
717 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
718 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
720 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
721 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
725 [o_bottombar_view setNeedsDisplay:YES];
728 - (void)toggleJumpButtons
730 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
732 if (b_show_jump_buttons)
733 [self addJumpButtons:NO];
735 [self removeJumpButtons:NO];
738 - (void)addJumpButtons:(BOOL)b_fast
740 NSRect preliminaryFrame = [o_bwd_btn frame];
741 BOOL b_enabled = [o_bwd_btn isEnabled];
742 preliminaryFrame.size.width = 29.;
743 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
744 [o_prev_btn setButtonType: NSMomentaryChangeButton];
745 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
746 [o_prev_btn setBordered:NO];
747 [o_prev_btn setTarget:self];
748 [o_prev_btn setAction:@selector(prev:)];
749 [o_prev_btn setToolTip: _NS("Previous")];
750 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
751 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
752 [o_prev_btn setEnabled: b_enabled];
754 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
755 [o_next_btn setButtonType: NSMomentaryChangeButton];
756 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
757 [o_next_btn setBordered:NO];
758 [o_next_btn setTarget:self];
759 [o_next_btn setAction:@selector(next:)];
760 [o_next_btn setToolTip: _NS("Next")];
761 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
762 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
763 [o_next_btn setEnabled: b_enabled];
765 if (b_dark_interface) {
766 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
767 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
768 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
769 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
771 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
772 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
773 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
774 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
778 frame = [o_bwd_btn frame];
780 [o_bwd_btn setFrame:frame];
781 frame = [o_fwd_btn frame];
783 [o_fwd_btn setFrame:frame];
785 #define moveItem(item) \
786 frame = [item frame]; \
787 frame.origin.x = frame.origin.x + f_space; \
789 [item setFrame: frame]; \
791 [[item animator] setFrame: frame]
796 moveItem(o_play_btn);
799 moveItem(o_stop_btn);
800 moveItem(o_playlist_btn);
801 moveItem(o_repeat_btn);
802 moveItem(o_shuffle_btn);
805 frame = [o_progress_view frame];
806 frame.size.width = frame.size.width - f_space;
807 frame.origin.x = frame.origin.x + f_space;
809 [o_progress_view setFrame: frame];
811 [[o_progress_view animator] setFrame: frame];
813 if (b_dark_interface) {
814 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
815 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
816 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
817 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
819 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
820 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
821 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
822 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
825 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;
826 [o_next_btn setFrame: preliminaryFrame];
828 // wait until the animation is done, if displayed
830 [o_bottombar_view addSubview:o_prev_btn];
831 [o_bottombar_view addSubview:o_next_btn];
833 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
834 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
837 [self toggleForwardBackwardMode: YES];
840 - (void)removeJumpButtons:(BOOL)b_fast
842 if (!o_prev_btn || !o_next_btn)
846 [o_prev_btn setHidden: YES];
847 [o_next_btn setHidden: YES];
849 [[o_prev_btn animator] setHidden: YES];
850 [[o_next_btn animator] setHidden: YES];
852 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
853 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
854 [o_prev_btn release];
856 [o_next_btn release];
860 frame = [o_bwd_btn frame];
862 [o_bwd_btn setFrame:frame];
863 frame = [o_fwd_btn frame];
865 [o_fwd_btn setFrame:frame];
867 #define moveItem(item) \
868 frame = [item frame]; \
869 frame.origin.x = frame.origin.x - f_space; \
871 [item setFrame: frame]; \
873 [[item animator] setFrame: frame]
878 moveItem(o_play_btn);
881 moveItem(o_stop_btn);
882 moveItem(o_playlist_btn);
883 moveItem(o_repeat_btn);
884 moveItem(o_shuffle_btn);
887 frame = [o_progress_view frame];
888 frame.size.width = frame.size.width + f_space;
889 frame.origin.x = frame.origin.x - f_space;
891 [o_progress_view setFrame: frame];
893 [[o_progress_view animator] setFrame: frame];
895 if (b_dark_interface) {
896 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
897 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
898 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
899 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
901 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
902 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
903 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
904 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
907 [self toggleForwardBackwardMode: NO];
909 [o_bottombar_view setNeedsDisplay:YES];
912 - (void)togglePlaymodeButtons
914 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
916 if (b_show_playmode_buttons)
917 [self addPlaymodeButtons:NO];
919 [self removePlaymodeButtons:NO];
922 - (void)addPlaymodeButtons:(BOOL)b_fast
925 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
927 if (b_dark_interface) {
928 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
929 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
931 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
932 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
934 frame = [o_playlist_btn frame];
936 [o_playlist_btn setFrame:frame];
939 [o_repeat_btn setHidden: NO];
940 [o_shuffle_btn setHidden: NO];
942 [[o_repeat_btn animator] setHidden: NO];
943 [[o_shuffle_btn animator] setHidden: NO];
946 frame = [o_progress_view frame];
947 frame.size.width = frame.size.width - f_space;
948 frame.origin.x = frame.origin.x + f_space;
950 [o_progress_view setFrame: frame];
952 [[o_progress_view animator] setFrame: frame];
955 - (void)removePlaymodeButtons:(BOOL)b_fast
958 float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
959 [o_repeat_btn setHidden: YES];
960 [o_shuffle_btn setHidden: YES];
962 if (b_dark_interface) {
963 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
964 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
966 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
967 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
969 frame = [o_playlist_btn frame];
971 [o_playlist_btn setFrame:frame];
973 frame = [o_progress_view frame];
974 frame.size.width = frame.size.width + f_space;
975 frame.origin.x = frame.origin.x - f_space;
977 [o_progress_view setFrame: frame];
979 [[o_progress_view animator] setFrame: frame];
983 #pragma mark Extra button actions
985 - (IBAction)stop:(id)sender
987 [[VLCCoreInteraction sharedInstance] stop];
990 // dynamically created next / prev buttons
991 - (IBAction)prev:(id)sender
993 [[VLCCoreInteraction sharedInstance] previous];
996 - (IBAction)next:(id)sender
998 [[VLCCoreInteraction sharedInstance] next];
1001 - (void)setRepeatOne
1003 [o_repeat_btn setImage: o_repeat_one_img];
1004 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1007 - (void)setRepeatAll
1009 [o_repeat_btn setImage: o_repeat_all_img];
1010 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1013 - (void)setRepeatOff
1015 [o_repeat_btn setImage: o_repeat_img];
1016 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1019 - (IBAction)repeat:(id)sender
1021 vlc_value_t looping,repeating;
1022 intf_thread_t * p_intf = VLCIntf;
1023 playlist_t * p_playlist = pl_Get(p_intf);
1025 var_Get(p_playlist, "repeat", &repeating);
1026 var_Get(p_playlist, "loop", &looping);
1028 if (!repeating.b_bool && !looping.b_bool) {
1029 /* was: no repeating at all, switching to Repeat One */
1030 [[VLCCoreInteraction sharedInstance] repeatOne];
1031 [self setRepeatOne];
1033 else if (repeating.b_bool && !looping.b_bool) {
1034 /* was: Repeat One, switching to Repeat All */
1035 [[VLCCoreInteraction sharedInstance] repeatAll];
1036 [self setRepeatAll];
1038 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1039 [[VLCCoreInteraction sharedInstance] repeatOff];
1040 [self setRepeatOff];
1047 playlist_t *p_playlist = pl_Get(VLCIntf);
1048 b_value = var_GetBool(p_playlist, "random");
1051 [o_shuffle_btn setImage: o_shuffle_on_img];
1052 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1054 [o_shuffle_btn setImage: o_shuffle_img];
1055 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1059 - (IBAction)shuffle:(id)sender
1061 [[VLCCoreInteraction sharedInstance] shuffle];
1065 - (IBAction)togglePlaylist:(id)sender
1067 [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent];
1070 - (IBAction)volumeAction:(id)sender
1072 if (sender == o_volume_sld)
1073 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1074 else if (sender == o_volume_down_btn)
1075 [[VLCCoreInteraction sharedInstance] toggleMute];
1077 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1080 - (IBAction)effects:(id)sender
1082 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1086 #pragma mark Extra updaters
1088 - (void)updateVolumeSlider
1090 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1091 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1094 [o_volume_sld setIntValue: i_volume];
1096 [o_volume_sld setIntValue: 0];
1098 [o_volume_sld setEnabled: !b_muted];
1099 [o_volume_up_btn setEnabled: !b_muted];
1102 - (void)updateControls
1104 [super updateControls];
1106 bool b_input = false;
1107 bool b_seekable = false;
1108 bool b_plmul = false;
1109 bool b_control = false;
1110 bool b_chapters = false;
1112 playlist_t * p_playlist = pl_Get(VLCIntf);
1115 b_plmul = playlist_CurrentSize(p_playlist) > 1;
1118 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
1119 if ((b_input = (p_input != NULL))) {
1120 /* seekable streams */
1121 b_seekable = var_GetBool(p_input, "can-seek");
1123 /* check whether slow/fast motion is possible */
1124 b_control = var_GetBool(p_input, "can-rate");
1126 /* chapters & titles */
1127 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1129 vlc_object_release(p_input);
1132 [o_stop_btn setEnabled: b_input];
1134 if (b_show_jump_buttons) {
1135 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1136 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1139 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];