1 /*****************************************************************************
2 * ControlsBar.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2012-2014 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"
30 #import "CompatibilityFixes.h"
32 /*****************************************************************************
33 * VLCControlsBarCommon
35 * Holds all outlets, actions and code common for controls bar in detached
37 *****************************************************************************/
39 @implementation VLCControlsBarCommon
41 @synthesize bottomBarView=o_bottombar_view;
45 b_dark_interface = config_GetInt(VLCIntf, "macosx-interfacestyle");
47 b_nativeFullscreenMode = NO;
48 #ifdef MAC_OS_X_VERSION_10_7
49 if (!OSX_SNOW_LEOPARD)
50 b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
53 [o_drop_view setDrawBorder: NO];
55 [o_play_btn setToolTip: _NS("Play/Pause")];
56 [[o_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
57 [[o_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
59 [o_bwd_btn setToolTip: _NS("Backward")];
60 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
61 [[o_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
63 [o_fwd_btn setToolTip: _NS("Forward")];
64 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
65 [[o_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
67 [o_time_sld setToolTip: _NS("Position")];
68 [[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];
69 [[o_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
71 [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
72 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
73 [[o_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
75 if (!b_dark_interface) {
76 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
78 [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns"]];
79 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-pressed"]];
80 o_play_img = [[NSImage imageNamed:@"play"] retain];
81 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
82 o_pause_img = [[NSImage imageNamed:@"pause"] retain];
83 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
84 [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns"]];
85 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-pressed"]];
87 [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"]];
88 [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
90 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
91 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
93 [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
95 [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns-dark"]];
96 [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-dark-pressed"]];
97 o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
98 o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
99 o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
100 o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
101 [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
102 [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
104 [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"]];
105 [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"]];
107 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
108 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
111 [o_play_btn setImage: o_play_img];
112 [o_play_btn setAlternateImage: o_play_pressed_img];
114 NSColor *o_string_color;
115 if (!var_InheritBool(VLCIntf, "macosx-interfacestyle"))
116 o_string_color = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
118 o_string_color = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
119 [o_time_fld setTextColor: o_string_color];
120 [o_time_fld setFont:[NSFont titleBarFontOfSize:10.0]];
121 [o_time_fld setAlignment: NSCenterTextAlignment];
122 [o_time_fld setNeedsDisplay:YES];
123 [o_time_fld setRemainingIdentifier:@"DisplayTimeAsTimeRemaining"];
125 // prepare time slider fance gradient view
126 if (!b_dark_interface) {
128 frame = [o_time_sld_fancygradient_view frame];
129 frame.size.height = frame.size.height - 1;
130 frame.origin.y = frame.origin.y + 1;
131 [o_time_sld_fancygradient_view setFrame: frame];
135 frame = [o_time_sld_fancygradient_view frame];
136 frame.size.width = 0;
137 [o_time_sld_fancygradient_view setFrame: frame];
139 // hide resize view if necessary
140 if (!OSX_SNOW_LEOPARD)
141 [o_resize_view setImage: NULL];
143 if ([[o_bottombar_view window] styleMask] & NSResizableWindowMask)
144 [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
147 // remove fullscreen button for lion fullscreen
148 if (b_nativeFullscreenMode) {
149 CGFloat f_width = [o_fullscreen_btn frame].size.width;
151 NSRect frame = [o_time_fld frame];
152 frame.origin.x += f_width;
153 [o_time_fld setFrame: frame];
155 frame = [o_progress_view frame];
156 frame.size.width = f_width + frame.size.width;
157 [o_progress_view setFrame: frame];
159 [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
162 if (config_GetInt(VLCIntf, "macosx-show-playback-buttons"))
163 [self toggleForwardBackwardMode: YES];
169 return [o_bottombar_view frame].size.height;
172 - (void)toggleForwardBackwardMode:(BOOL)b_alt
175 /* change the accessibility help for the backward/forward buttons accordingly */
176 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
177 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
179 [o_fwd_btn setAction:@selector(alternateForward:)];
180 [o_bwd_btn setAction:@selector(alternateBackward:)];
183 /* change the accessibility help for the backward/forward buttons accordingly */
184 [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
185 [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
187 [o_fwd_btn setAction:@selector(fwd:)];
188 [o_bwd_btn setAction:@selector(bwd:)];
193 #pragma mark Button Actions
195 - (IBAction)play:(id)sender
197 [[VLCCoreInteraction sharedInstance] playOrPause];
200 - (void)resetPreviousButton
202 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
203 // seems like no further event occurred, so let's switch the playback item
204 [[VLCCoreInteraction sharedInstance] previous];
205 just_triggered_previous = NO;
209 - (void)resetBackwardSkip
211 // the user stopped skipping, so let's allow him to change the item
212 if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
213 just_triggered_previous = NO;
216 - (IBAction)bwd:(id)sender
218 if (!just_triggered_previous) {
219 just_triggered_previous = YES;
220 [self performSelector:@selector(resetPreviousButton)
224 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
225 // we just skipped 4 "continous" events, otherwise we are too fast
226 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
227 last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
228 [self performSelector:@selector(resetBackwardSkip)
235 - (void)resetNextButton
237 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
238 // seems like no further event occurred, so let's switch the playback item
239 [[VLCCoreInteraction sharedInstance] next];
240 just_triggered_next = NO;
244 - (void)resetForwardSkip
246 // the user stopped skipping, so let's allow him to change the item
247 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
248 just_triggered_next = NO;
251 - (IBAction)fwd:(id)sender
253 if (!just_triggered_next) {
254 just_triggered_next = YES;
255 [self performSelector:@selector(resetNextButton)
259 if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
260 // we just skipped 4 "continous" events, otherwise we are too fast
261 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
262 last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
263 [self performSelector:@selector(resetForwardSkip)
270 // alternative actions for forward / backward buttons when next / prev are activated
271 - (IBAction)alternateForward:(id)sender
273 [[VLCCoreInteraction sharedInstance] forwardExtraShort];
276 - (IBAction)alternateBackward:(id)sender
278 [[VLCCoreInteraction sharedInstance] backwardExtraShort];
281 - (IBAction)timeSliderAction:(id)sender
284 input_thread_t * p_input;
286 switch([[NSApp currentEvent] type]) {
288 case NSLeftMouseDown:
289 case NSLeftMouseDragged:
290 f_updated = [sender floatValue];
296 p_input = pl_CurrentInput(VLCIntf);
297 if (p_input != NULL) {
301 pos.f_float = f_updated / 10000.;
302 var_Set(p_input, "position", pos);
303 [o_time_sld setFloatValue: f_updated];
305 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
306 [o_time_fld setStringValue: o_time];
307 vlc_object_release(p_input);
311 - (IBAction)fullscreen:(id)sender
313 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
317 #pragma mark Updaters
319 - (void)updateTimeSlider
321 input_thread_t * p_input;
322 p_input = pl_CurrentInput(VLCIntf);
328 var_Get(p_input, "position", &pos);
329 f_updated = 10000. * pos.f_float;
330 [o_time_sld setFloatValue: f_updated];
332 o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
334 mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
336 [o_time_sld setHidden: YES];
337 [o_time_sld_fancygradient_view setHidden: YES];
339 if ([o_time_sld isHidden] == YES) {
340 bool b_buffering = false;
341 input_state_e inputState = input_GetState(p_input);
342 if (inputState == INIT_S || inputState == OPENING_S)
345 [o_time_sld setHidden: b_buffering];
346 [o_time_sld_fancygradient_view setHidden: b_buffering];
349 [o_time_fld setStringValue: o_time];
350 [o_time_fld setNeedsDisplay:YES];
352 vlc_object_release(p_input);
354 [o_time_sld setFloatValue: 0.0];
355 [o_time_fld setStringValue: @"00:00"];
356 [o_time_sld setHidden: YES];
357 [o_time_sld_fancygradient_view setHidden: YES];
361 - (void)drawFancyGradientEffectForTimeSlider
363 NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
364 CGFloat f_value = [o_time_sld knobPosition];
366 NSRect oldFrame = [o_time_sld_fancygradient_view frame];
367 if (f_value != oldFrame.size.width) {
368 if ([o_time_sld_fancygradient_view isHidden])
369 [o_time_sld_fancygradient_view setHidden: NO];
370 [o_time_sld_fancygradient_view setFrame: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height)];
374 frame = [o_time_sld_fancygradient_view frame];
375 if (frame.size.width > 0) {
376 frame.size.width = 0;
377 [o_time_sld_fancygradient_view setFrame: frame];
379 [o_time_sld_fancygradient_view setHidden: YES];
384 - (void)updateControls
386 bool b_plmul = false;
387 bool b_seekable = false;
388 bool b_chapters = false;
389 bool b_buffering = false;
391 playlist_t * p_playlist = pl_Get(VLCIntf);
394 b_plmul = playlist_CurrentSize(p_playlist) > 1;
397 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
401 input_state_e inputState = input_GetState(p_input);
402 if (inputState == INIT_S || inputState == OPENING_S)
405 /* seekable streams */
406 b_seekable = var_GetBool(p_input, "can-seek");
408 /* chapters & titles */
409 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
411 vlc_object_release(p_input);
416 [o_progress_bar startAnimation:self];
417 [o_progress_bar setIndeterminate:YES];
418 [o_progress_bar setHidden:NO];
420 [o_progress_bar stopAnimation:self];
421 [o_progress_bar setHidden:YES];
424 [o_time_sld setEnabled: b_seekable];
426 [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
427 [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
432 [o_play_btn setImage: o_pause_img];
433 [o_play_btn setAlternateImage: o_pause_pressed_img];
434 [o_play_btn setToolTip: _NS("Pause")];
439 [o_play_btn setImage: o_play_img];
440 [o_play_btn setAlternateImage: o_play_pressed_img];
441 [o_play_btn setToolTip: _NS("Play")];
444 - (void)setFullscreenState:(BOOL)b_fullscreen
446 if (!b_nativeFullscreenMode)
447 [o_fullscreen_btn setState:b_fullscreen];
451 [o_play_img release];
452 [o_play_pressed_img release];
453 [o_pause_img release];
454 [o_pause_pressed_img release];
461 /*****************************************************************************
462 * VLCMainWindowControlsBar
464 * Holds all specific outlets, actions and code for the main window controls bar.
465 *****************************************************************************/
467 @interface VLCMainWindowControlsBar (Internal)
468 - (void)addJumpButtons:(BOOL)b_fast;
469 - (void)removeJumpButtons:(BOOL)b_fast;
470 - (void)addPlaymodeButtons:(BOOL)b_fast;
471 - (void)removePlaymodeButtons:(BOOL)b_fast;
474 @implementation VLCMainWindowControlsBar
478 [super awakeFromNib];
481 [o_stop_btn setToolTip: _NS("Stop")];
482 [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
483 [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
485 [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
486 [[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];
487 [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
489 [o_repeat_btn setToolTip: _NS("Repeat")];
490 [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
491 [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
493 [o_shuffle_btn setToolTip: _NS("Shuffle")];
494 [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
495 [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
497 [o_volume_sld setToolTip: _NS("Volume")];
498 [[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];
499 [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
500 [o_volume_down_btn setToolTip: _NS("Mute")];
501 [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
502 [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
503 [o_volume_up_btn setToolTip: _NS("Full Volume")];
504 [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
505 [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
507 [o_effects_btn setToolTip: _NS("Audio Effects")];
508 [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
509 [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
511 if (!b_dark_interface) {
512 [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
513 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
515 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
516 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
517 o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
518 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
519 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all"] retain];
520 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
521 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
522 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
523 o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
524 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
525 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
526 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
528 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
529 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
530 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
531 [o_volume_sld setUsesBrightArtwork: YES];
533 if (b_nativeFullscreenMode) {
534 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
535 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed"]];
537 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
538 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
541 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
542 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
544 [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
545 [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
547 [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
548 [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
549 o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
550 o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
551 o_repeat_all_img = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
552 o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
553 o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
554 o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
555 o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
556 o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
557 o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
558 o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
560 [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
561 [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
562 [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
563 [o_volume_sld setUsesBrightArtwork: NO];
565 if (b_nativeFullscreenMode) {
566 [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
567 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed-dark"]];
569 [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
570 [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
573 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
574 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
576 [o_repeat_btn setImage: o_repeat_img];
577 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
578 [o_shuffle_btn setImage: o_shuffle_img];
579 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
581 BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
582 [o_volume_sld setEnabled: b_mute];
583 [o_volume_sld setMaxValue: [[VLCCoreInteraction sharedInstance] maxVolume]];
584 [o_volume_up_btn setEnabled: b_mute];
586 // remove fullscreen button for lion fullscreen
587 if (b_nativeFullscreenMode) {
590 // == [o_fullscreen_btn frame].size.width;
591 // button is already removed!
593 #define moveItem(item) \
594 frame = [item frame]; \
595 frame.origin.x = f_width + frame.origin.x; \
596 [item setFrame: frame]
598 moveItem(o_effects_btn);
599 moveItem(o_volume_up_btn);
600 moveItem(o_volume_sld);
601 moveItem(o_volume_track_view);
602 moveItem(o_volume_down_btn);
605 // time field and progress bar are moved in super method!
609 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
610 if (b_show_jump_buttons)
611 [self addJumpButtons:YES];
613 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
614 if (!b_show_playmode_buttons)
615 [self removePlaymodeButtons:YES];
617 if (!config_GetInt(VLCIntf, "macosx-show-effects-button"))
618 [self removeEffectsButton:YES];
620 [[VLCMain sharedInstance] playbackModeUpdated];
625 #pragma mark interface customization
628 - (void)toggleEffectsButton
630 if (config_GetInt(VLCIntf, "macosx-show-effects-button"))
631 [self addEffectsButton:NO];
633 [self removeEffectsButton:NO];
636 - (void)addEffectsButton:(BOOL)b_fast
642 [o_effects_btn setHidden: NO];
644 [[o_effects_btn animator] setHidden: NO];
647 #define moveItem(item) \
648 frame = [item frame]; \
649 frame.origin.x = frame.origin.x - f_space; \
651 [item setFrame: frame]; \
653 [[item animator] setFrame: frame]
656 CGFloat f_space = [o_effects_btn frame].size.width;
657 // extra margin between button and volume up button
658 if (b_nativeFullscreenMode)
662 moveItem(o_volume_up_btn);
663 moveItem(o_volume_sld);
664 moveItem(o_volume_track_view);
665 moveItem(o_volume_down_btn);
666 moveItem(o_time_fld);
670 frame = [o_progress_view frame];
671 frame.size.width = frame.size.width - f_space;
673 [o_progress_view setFrame: frame];
675 [[o_progress_view animator] setFrame: frame];
677 if (!b_nativeFullscreenMode) {
678 if (b_dark_interface) {
679 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
680 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
682 [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
683 [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
687 [o_bottombar_view setNeedsDisplay:YES];
690 - (void)removeEffectsButton:(BOOL)b_fast
695 [o_effects_btn setHidden: YES];
697 #define moveItem(item) \
698 frame = [item frame]; \
699 frame.origin.x = frame.origin.x + f_space; \
701 [item setFrame: frame]; \
703 [[item animator] setFrame: frame]
706 CGFloat f_space = [o_effects_btn frame].size.width;
707 // extra margin between button and volume up button
708 if (b_nativeFullscreenMode)
711 moveItem(o_volume_up_btn);
712 moveItem(o_volume_sld);
713 moveItem(o_volume_track_view);
714 moveItem(o_volume_down_btn);
715 moveItem(o_time_fld);
719 frame = [o_progress_view frame];
720 frame.size.width = frame.size.width + f_space;
722 [o_progress_view setFrame: frame];
724 [[o_progress_view animator] setFrame: frame];
726 if (!b_nativeFullscreenMode) {
727 if (b_dark_interface) {
728 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
729 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
731 [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
732 [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
736 [o_bottombar_view setNeedsDisplay:YES];
739 - (void)toggleJumpButtons
741 b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
743 if (b_show_jump_buttons)
744 [self addJumpButtons:NO];
746 [self removeJumpButtons:NO];
749 - (void)addJumpButtons:(BOOL)b_fast
751 NSRect preliminaryFrame = [o_bwd_btn frame];
752 BOOL b_enabled = [o_bwd_btn isEnabled];
753 preliminaryFrame.size.width = 29.;
754 o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
755 [o_prev_btn setButtonType: NSMomentaryChangeButton];
756 [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
757 [o_prev_btn setBordered:NO];
758 [o_prev_btn setTarget:self];
759 [o_prev_btn setAction:@selector(prev:)];
760 [o_prev_btn setToolTip: _NS("Previous")];
761 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
762 [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
763 [o_prev_btn setEnabled: b_enabled];
765 o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
766 [o_next_btn setButtonType: NSMomentaryChangeButton];
767 [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
768 [o_next_btn setBordered:NO];
769 [o_next_btn setTarget:self];
770 [o_next_btn setAction:@selector(next:)];
771 [o_next_btn setToolTip: _NS("Next")];
772 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
773 [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
774 [o_next_btn setEnabled: b_enabled];
776 if (b_dark_interface) {
777 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
778 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
779 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
780 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
782 [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
783 [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
784 [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
785 [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
789 frame = [o_bwd_btn frame];
791 [o_bwd_btn setFrame:frame];
792 frame = [o_fwd_btn frame];
794 [o_fwd_btn setFrame:frame];
796 #define moveItem(item) \
797 frame = [item frame]; \
798 frame.origin.x = frame.origin.x + f_space; \
800 [item setFrame: frame]; \
802 [[item animator] setFrame: frame]
807 moveItem(o_play_btn);
810 moveItem(o_stop_btn);
811 moveItem(o_playlist_btn);
812 moveItem(o_repeat_btn);
813 moveItem(o_shuffle_btn);
816 frame = [o_progress_view frame];
817 frame.size.width = frame.size.width - f_space;
818 frame.origin.x = frame.origin.x + f_space;
820 [o_progress_view setFrame: frame];
822 [[o_progress_view animator] setFrame: frame];
824 if (b_dark_interface) {
825 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
826 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
827 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
828 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
830 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
831 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
832 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
833 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
836 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;
837 [o_next_btn setFrame: preliminaryFrame];
839 // wait until the animation is done, if displayed
841 [o_bottombar_view addSubview:o_prev_btn];
842 [o_bottombar_view addSubview:o_next_btn];
844 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
845 [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
848 [self toggleForwardBackwardMode: YES];
851 - (void)removeJumpButtons:(BOOL)b_fast
853 if (!o_prev_btn || !o_next_btn)
857 [o_prev_btn setHidden: YES];
858 [o_next_btn setHidden: YES];
860 [[o_prev_btn animator] setHidden: YES];
861 [[o_next_btn animator] setHidden: YES];
863 [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
864 [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
865 [o_prev_btn release];
867 [o_next_btn release];
871 frame = [o_bwd_btn frame];
873 [o_bwd_btn setFrame:frame];
874 frame = [o_fwd_btn frame];
876 [o_fwd_btn setFrame:frame];
878 #define moveItem(item) \
879 frame = [item frame]; \
880 frame.origin.x = frame.origin.x - f_space; \
882 [item setFrame: frame]; \
884 [[item animator] setFrame: frame]
889 moveItem(o_play_btn);
892 moveItem(o_stop_btn);
893 moveItem(o_playlist_btn);
894 moveItem(o_repeat_btn);
895 moveItem(o_shuffle_btn);
898 frame = [o_progress_view frame];
899 frame.size.width = frame.size.width + f_space;
900 frame.origin.x = frame.origin.x - f_space;
902 [o_progress_view setFrame: frame];
904 [[o_progress_view animator] setFrame: frame];
906 if (b_dark_interface) {
907 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
908 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
909 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
910 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
912 [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
913 [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
914 [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
915 [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
918 [self toggleForwardBackwardMode: NO];
920 [o_bottombar_view setNeedsDisplay:YES];
923 - (void)togglePlaymodeButtons
925 b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
927 if (b_show_playmode_buttons)
928 [self addPlaymodeButtons:NO];
930 [self removePlaymodeButtons:NO];
933 - (void)addPlaymodeButtons:(BOOL)b_fast
936 CGFloat f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
938 if (b_dark_interface) {
939 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
940 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
942 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
943 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
945 frame = [o_playlist_btn frame];
947 [o_playlist_btn setFrame:frame];
950 [o_repeat_btn setHidden: NO];
951 [o_shuffle_btn setHidden: NO];
953 [[o_repeat_btn animator] setHidden: NO];
954 [[o_shuffle_btn animator] setHidden: NO];
957 frame = [o_progress_view frame];
958 frame.size.width = frame.size.width - f_space;
959 frame.origin.x = frame.origin.x + f_space;
961 [o_progress_view setFrame: frame];
963 [[o_progress_view animator] setFrame: frame];
966 - (void)removePlaymodeButtons:(BOOL)b_fast
969 CGFloat f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
970 [o_repeat_btn setHidden: YES];
971 [o_shuffle_btn setHidden: YES];
973 if (b_dark_interface) {
974 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
975 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
977 [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
978 [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
980 frame = [o_playlist_btn frame];
982 [o_playlist_btn setFrame:frame];
984 frame = [o_progress_view frame];
985 frame.size.width = frame.size.width + f_space;
986 frame.origin.x = frame.origin.x - f_space;
988 [o_progress_view setFrame: frame];
990 [[o_progress_view animator] setFrame: frame];
994 #pragma mark Extra button actions
996 - (IBAction)stop:(id)sender
998 [[VLCCoreInteraction sharedInstance] stop];
1001 // dynamically created next / prev buttons
1002 - (IBAction)prev:(id)sender
1004 [[VLCCoreInteraction sharedInstance] previous];
1007 - (IBAction)next:(id)sender
1009 [[VLCCoreInteraction sharedInstance] next];
1012 - (void)setRepeatOne
1014 [o_repeat_btn setImage: o_repeat_one_img];
1015 [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1018 - (void)setRepeatAll
1020 [o_repeat_btn setImage: o_repeat_all_img];
1021 [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1024 - (void)setRepeatOff
1026 [o_repeat_btn setImage: o_repeat_img];
1027 [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1030 - (IBAction)repeat:(id)sender
1032 vlc_value_t looping,repeating;
1033 intf_thread_t * p_intf = VLCIntf;
1034 playlist_t * p_playlist = pl_Get(p_intf);
1036 var_Get(p_playlist, "repeat", &repeating);
1037 var_Get(p_playlist, "loop", &looping);
1039 if (!repeating.b_bool && !looping.b_bool) {
1040 /* was: no repeating at all, switching to Repeat One */
1041 [[VLCCoreInteraction sharedInstance] repeatOne];
1042 [self setRepeatOne];
1044 else if (repeating.b_bool && !looping.b_bool) {
1045 /* was: Repeat One, switching to Repeat All */
1046 [[VLCCoreInteraction sharedInstance] repeatAll];
1047 [self setRepeatAll];
1049 /* was: Repeat All or bug in VLC, switching to Repeat Off */
1050 [[VLCCoreInteraction sharedInstance] repeatOff];
1051 [self setRepeatOff];
1058 playlist_t *p_playlist = pl_Get(VLCIntf);
1059 b_value = var_GetBool(p_playlist, "random");
1062 [o_shuffle_btn setImage: o_shuffle_on_img];
1063 [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1065 [o_shuffle_btn setImage: o_shuffle_img];
1066 [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1070 - (IBAction)shuffle:(id)sender
1072 [[VLCCoreInteraction sharedInstance] shuffle];
1076 - (IBAction)togglePlaylist:(id)sender
1078 [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent];
1081 - (IBAction)volumeAction:(id)sender
1083 if (sender == o_volume_sld)
1084 [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1085 else if (sender == o_volume_down_btn)
1086 [[VLCCoreInteraction sharedInstance] toggleMute];
1088 [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1091 - (IBAction)effects:(id)sender
1093 [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1097 #pragma mark Extra updaters
1099 - (void)updateVolumeSlider
1101 int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1102 BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1105 [o_volume_sld setIntValue: i_volume];
1107 [o_volume_sld setIntValue: 0];
1109 [o_volume_sld setEnabled: !b_muted];
1110 [o_volume_up_btn setEnabled: !b_muted];
1113 - (void)updateControls
1115 [super updateControls];
1117 bool b_input = false;
1118 bool b_seekable = false;
1119 bool b_plmul = false;
1120 bool b_control = false;
1121 bool b_chapters = false;
1123 playlist_t * p_playlist = pl_Get(VLCIntf);
1126 b_plmul = playlist_CurrentSize(p_playlist) > 1;
1129 input_thread_t * p_input = playlist_CurrentInput(p_playlist);
1130 if ((b_input = (p_input != NULL))) {
1131 /* seekable streams */
1132 b_seekable = var_GetBool(p_input, "can-seek");
1134 /* check whether slow/fast motion is possible */
1135 b_control = var_GetBool(p_input, "can-rate");
1137 /* chapters & titles */
1138 //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1140 vlc_object_release(p_input);
1143 [o_stop_btn setEnabled: b_input];
1145 if (b_show_jump_buttons) {
1146 [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1147 [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1150 [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1154 [o_repeat_img release];
1155 [o_repeat_pressed_img release];
1156 [o_repeat_all_img release];
1157 [o_repeat_all_pressed_img release];
1158 [o_repeat_one_img release];
1159 [o_repeat_one_pressed_img release];
1160 [o_shuffle_img release];
1161 [o_shuffle_pressed_img release];
1162 [o_shuffle_on_img release];
1163 [o_shuffle_on_pressed_img release];