]> git.sesse.net Git - vlc/blob - modules/gui/macosx/ControlsBar.m
macosx: fix position and functionality of buttons when jump buttons are activated
[vlc] / modules / gui / macosx / ControlsBar.m
1 /*****************************************************************************
2  * ControlsBar.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #import "ControlsBar.h"
26 #import "intf.h"
27 #import "CoreInteraction.h"
28 #import "MainMenu.h"
29 #import "fspanel.h"
30
31 /*****************************************************************************
32  * VLCControlsBarCommon
33  *
34  *  Holds all outlets, actions and code common for controls bar in detached
35  *  and in main window.
36  *****************************************************************************/
37
38 @implementation VLCControlsBarCommon
39
40 @synthesize bottomBarView=o_bottombar_view;
41
42 - (void)awakeFromNib
43 {
44     b_dark_interface = config_GetInt(VLCIntf, "macosx-interfacestyle");
45
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");
50 #endif
51
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];
55
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];
59
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];
63
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];
67
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];
71
72     if (!b_dark_interface) {
73         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
74
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"]];
83
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"]];
86
87         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
88         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
89     } else {
90         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
91
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"]];
100
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"]];
103
104         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
105         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
106     }
107
108     [o_play_btn setImage: o_play_img];
109     [o_play_btn setAlternateImage: o_play_pressed_img];
110
111     [o_time_fld setAlignment: NSCenterTextAlignment];
112     [o_time_fld setNeedsDisplay:YES];
113
114     // prepare time slider fance gradient view
115     if (!b_dark_interface) {
116         NSRect frame;
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];
121     }
122
123     NSRect frame;
124     frame = [o_time_sld_fancygradient_view frame];
125     frame.size.width = 0;
126     [o_time_sld_fancygradient_view setFrame: frame];
127
128     // hide resize view if necessary
129     if (!OSX_SNOW_LEOPARD)
130         [o_resize_view setImage: NULL];
131
132     if ([[o_bottombar_view window] styleMask] & NSResizableWindowMask)
133         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
134
135 }
136
137 #pragma mark -
138 #pragma mark Button Actions
139
140 - (IBAction)play:(id)sender
141 {
142     [[VLCCoreInteraction sharedInstance] play];
143 }
144
145 - (void)resetPreviousButton
146 {
147     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
148         // seems like no further event occurred, so let's switch the playback item
149         [[VLCCoreInteraction sharedInstance] previous];
150         just_triggered_previous = NO;
151     }
152 }
153
154 - (void)resetBackwardSkip
155 {
156     // the user stopped skipping, so let's allow him to change the item
157     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
158         just_triggered_previous = NO;
159 }
160
161 - (IBAction)bwd:(id)sender
162 {
163     if (!just_triggered_previous) {
164         just_triggered_previous = YES;
165         [self performSelector:@selector(resetPreviousButton)
166                    withObject: NULL
167                    afterDelay:0.40];
168     } else {
169         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
170             // we just skipped 4 "continous" events, otherwise we are too fast
171             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
172             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
173             [self performSelector:@selector(resetBackwardSkip)
174                        withObject: NULL
175                        afterDelay:0.40];
176         }
177     }
178 }
179
180 - (void)resetNextButton
181 {
182     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
183         // seems like no further event occurred, so let's switch the playback item
184         [[VLCCoreInteraction sharedInstance] next];
185         just_triggered_next = NO;
186     }
187 }
188
189 - (void)resetForwardSkip
190 {
191     // the user stopped skipping, so let's allow him to change the item
192     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
193         just_triggered_next = NO;
194 }
195
196 - (IBAction)fwd:(id)sender
197 {
198     if (!just_triggered_next) {
199         just_triggered_next = YES;
200         [self performSelector:@selector(resetNextButton)
201                    withObject: NULL
202                    afterDelay:0.40];
203     } else {
204         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
205             // we just skipped 4 "continous" events, otherwise we are too fast
206             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
207             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
208             [self performSelector:@selector(resetForwardSkip)
209                        withObject: NULL
210                        afterDelay:0.40];
211         }
212     }
213 }
214
215 - (IBAction)timeSliderAction:(id)sender
216 {
217     float f_updated;
218     input_thread_t * p_input;
219
220     switch([[NSApp currentEvent] type]) {
221         case NSLeftMouseUp:
222         case NSLeftMouseDown:
223         case NSLeftMouseDragged:
224             f_updated = [sender floatValue];
225             break;
226
227         default:
228             return;
229     }
230     p_input = pl_CurrentInput(VLCIntf);
231     if (p_input != NULL) {
232         vlc_value_t pos;
233         NSString * o_time;
234
235         pos.f_float = f_updated / 10000.;
236         var_Set(p_input, "position", pos);
237         [o_time_sld setFloatValue: f_updated];
238
239         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
240         [o_time_fld setStringValue: o_time];
241         vlc_object_release(p_input);
242     }
243 }
244
245 - (IBAction)fullscreen:(id)sender
246 {
247     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
248 }
249
250 #pragma mark -
251 #pragma mark Updaters
252
253 - (void)updateTimeSlider
254 {
255     input_thread_t * p_input;
256     p_input = pl_CurrentInput(VLCIntf);
257     if (p_input) {
258         NSString * o_time;
259         vlc_value_t pos;
260         float f_updated;
261
262         var_Get(p_input, "position", &pos);
263         f_updated = 10000. * pos.f_float;
264         [o_time_sld setFloatValue: f_updated];
265
266         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
267
268         mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
269         if (dur == -1) {
270             [o_time_sld setEnabled: NO];
271             [o_time_sld setHidden: YES];
272             [o_time_sld_fancygradient_view setHidden: YES];
273         } else {
274             [o_time_sld setEnabled: YES];
275             [o_time_sld setHidden: NO];
276             [o_time_sld_fancygradient_view setHidden: NO];
277         }
278
279         [o_time_fld setStringValue: o_time];
280         [o_time_fld setNeedsDisplay:YES];
281         vlc_object_release(p_input);
282     } else {
283         [o_time_sld setFloatValue: 0.0];
284         [o_time_fld setStringValue: @"00:00"];
285         [o_time_sld setEnabled: NO];
286         [o_time_sld setHidden: YES];
287         [o_time_sld_fancygradient_view setHidden: YES];
288     }
289 }
290
291 - (void)drawFancyGradientEffectForTimeSlider
292 {
293     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
294     CGFloat f_value = [o_time_sld knobPosition];
295     if (f_value > 7.5) {
296         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
297         if (f_value != oldFrame.size.width) {
298             if ([o_time_sld_fancygradient_view isHidden])
299                 [o_time_sld_fancygradient_view setHidden: NO];
300             [o_time_sld_fancygradient_view setFrame: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height)];
301         }
302     } else {
303         NSRect frame;
304         frame = [o_time_sld_fancygradient_view frame];
305         if (frame.size.width > 0) {
306             frame.size.width = 0;
307             [o_time_sld_fancygradient_view setFrame: frame];
308         }
309         [o_time_sld_fancygradient_view setHidden: YES];
310     }
311     [o_pool release];
312 }
313
314 - (void)updateControls
315 {
316     bool b_plmul = false;
317     bool b_seekable = false;
318     bool b_chapters = false;
319
320     playlist_t * p_playlist = pl_Get(VLCIntf);
321
322     PL_LOCK;
323     b_plmul = playlist_CurrentSize(p_playlist) > 1;
324     PL_UNLOCK;
325
326     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
327
328
329     if (p_input) {
330         /* seekable streams */
331         b_seekable = var_GetBool(p_input, "can-seek");
332
333         /* chapters & titles */
334         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
335
336         vlc_object_release(p_input);
337     }
338
339     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
340     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
341 }
342
343 - (void)setPause
344 {
345     [o_play_btn setImage: o_pause_img];
346     [o_play_btn setAlternateImage: o_pause_pressed_img];
347     [o_play_btn setToolTip: _NS("Pause")];
348 }
349
350 - (void)setPlay
351 {
352     [o_play_btn setImage: o_play_img];
353     [o_play_btn setAlternateImage: o_play_pressed_img];
354     [o_play_btn setToolTip: _NS("Play")];
355 }
356
357 - (void)setFullscreenState:(BOOL)b_fullscreen
358 {
359     if (!b_nativeFullscreenMode)
360         [o_fullscreen_btn setState:b_fullscreen];
361 }
362
363 @end
364
365
366 /*****************************************************************************
367  * VLCMainWindowControlsBar
368  *
369  *  Holds all specific outlets, actions and code for the main window controls bar.
370  *****************************************************************************/
371
372 @interface VLCMainWindowControlsBar (Internal)
373 - (void)addJumpButtons:(BOOL)b_fast;
374 - (void)removeJumpButtons:(BOOL)b_fast;
375 - (void)addPlaymodeButtons:(BOOL)b_fast;
376 - (void)removePlaymodeButtons:(BOOL)b_fast;
377 @end
378
379 @implementation VLCMainWindowControlsBar
380
381 - (void)awakeFromNib
382 {
383     [super awakeFromNib];
384
385     [o_stop_btn setToolTip: _NS("Stop")];
386     [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
387     [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
388
389     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
390     [[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];
391     [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
392
393     [o_repeat_btn setToolTip: _NS("Repeat")];
394     [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
395     [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
396
397     [o_shuffle_btn setToolTip: _NS("Shuffle")];
398     [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
399     [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
400
401     [o_volume_sld setToolTip: _NS("Volume")];
402     [[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];
403     [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
404     [o_volume_down_btn setToolTip: _NS("Mute")];
405     [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
406     [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
407     [o_volume_up_btn setToolTip: _NS("Full Volume")];
408     [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
409     [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
410
411     [o_effects_btn setToolTip: _NS("Effects")];
412     [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
413     [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
414
415     if (!b_dark_interface) {
416         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
417         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
418
419         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
420         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
421         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
422         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
423         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
424         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
425         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
426         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
427         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
428         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
429         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
430         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
431
432         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
433         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
434         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
435
436         if (b_nativeFullscreenMode) {
437             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
438             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed"]];
439         } else {
440             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
441             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
442         }
443
444         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
445         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
446     } else {
447         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
448         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
449
450         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
451         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
452         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
453         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
454         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
455         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
456         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
457         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
458         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
459         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
460         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
461         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
462
463         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
464         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
465         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
466
467         if (b_nativeFullscreenMode) {
468             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
469             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-pressed-dark"]];
470         } else {
471             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
472             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
473         }
474
475         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
476         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
477     }
478     [o_repeat_btn setImage: o_repeat_img];
479     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
480     [o_shuffle_btn setImage: o_shuffle_img];
481     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
482
483     BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
484     [o_volume_sld setEnabled: b_mute];
485     [o_volume_up_btn setEnabled: b_mute];
486
487     // remove fullscreen button for lion fullscreen
488     if (b_nativeFullscreenMode) {
489         NSRect frame;
490         float f_width = [o_fullscreen_btn frame].size.width;
491
492 #define moveItem(item) \
493 frame = [item frame]; \
494 frame.origin.x = f_width + frame.origin.x; \
495 [item setFrame: frame]
496
497         moveItem(o_effects_btn);
498         moveItem(o_volume_up_btn);
499         moveItem(o_volume_sld);
500         moveItem(o_volume_track_view);
501         moveItem(o_volume_down_btn);
502         moveItem(o_time_fld);
503 #undef moveItem
504
505         frame = [o_progress_view frame];
506         frame.size.width = f_width + frame.size.width;
507         [o_progress_view setFrame: frame];
508
509         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
510     }
511
512     b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
513     if (b_show_jump_buttons)
514         [self addJumpButtons:YES];
515
516     b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
517     if (!b_show_playmode_buttons)
518         [self removePlaymodeButtons:YES];
519
520     [[VLCMain sharedInstance] playbackModeUpdated];
521 }
522
523 #pragma mark -
524 #pragma mark interface customization
525
526 - (void)toggleJumpButtons
527 {
528     b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
529
530     if (b_show_jump_buttons)
531         [self addJumpButtons:NO];
532     else
533         [self removeJumpButtons:NO];
534 }
535
536 - (void)addJumpButtons:(BOOL)b_fast
537 {
538     NSRect preliminaryFrame = [o_bwd_btn frame];
539     BOOL b_enabled = [o_bwd_btn isEnabled];
540     preliminaryFrame.size.width = 29.;
541     o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
542     [o_prev_btn setButtonType: NSMomentaryChangeButton];
543     [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
544     [o_prev_btn setBordered:NO];
545     [o_prev_btn setTarget:self];
546     [o_prev_btn setAction:@selector(prev:)];
547     [o_prev_btn setToolTip: _NS("Previous")];
548     [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
549     [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
550     [o_prev_btn setEnabled: b_enabled];
551
552     o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
553     [o_next_btn setButtonType: NSMomentaryChangeButton];
554     [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
555     [o_next_btn setBordered:NO];
556     [o_next_btn setTarget:self];
557     [o_next_btn setAction:@selector(next:)];
558     [o_next_btn setToolTip: _NS("Next")];
559     [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
560     [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
561     [o_next_btn setEnabled: b_enabled];
562
563     if (b_dark_interface) {
564         [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
565         [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
566         [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
567         [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
568     } else {
569         [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
570         [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
571         [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
572         [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
573     }
574
575     /* change the accessibility help for the backward/forward buttons accordingly */
576     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
577     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
578
579     NSRect frame;
580     frame = [o_bwd_btn frame];
581     frame.size.width--;
582     [o_bwd_btn setFrame:frame];
583     frame = [o_fwd_btn frame];
584     frame.size.width--;
585     [o_fwd_btn setFrame:frame];
586
587 #define moveItem(item) \
588 frame = [item frame]; \
589 frame.origin.x = frame.origin.x + f_space; \
590 if (b_fast) \
591     [item setFrame: frame]; \
592 else \
593     [[item animator] setFrame: frame]
594     
595     float f_space = 29.;
596     moveItem(o_bwd_btn);
597     f_space = 28.;
598     moveItem(o_play_btn);
599     moveItem(o_fwd_btn);
600     f_space = 28. * 2;
601     moveItem(o_stop_btn);
602     moveItem(o_playlist_btn);
603     moveItem(o_repeat_btn);
604     moveItem(o_shuffle_btn);
605 #undef moveItem
606
607     frame = [o_progress_view frame];
608     frame.size.width = frame.size.width - f_space;
609     frame.origin.x = frame.origin.x + f_space;
610     if (b_fast)
611         [o_progress_view setFrame: frame];
612     else
613         [[o_progress_view animator] setFrame: frame];
614
615     if (b_dark_interface) {
616         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
617         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
618         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
619         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
620     } else {
621         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
622         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
623         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
624         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
625     }
626
627     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;
628     [o_next_btn setFrame: preliminaryFrame];
629
630     // wait until the animation is done, if displayed
631     if (b_fast) {
632         [o_bottombar_view addSubview:o_prev_btn];
633         [o_bottombar_view addSubview:o_next_btn];
634     } else {
635         [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
636         [o_bottombar_view performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
637     }
638
639     [o_fwd_btn setAction:@selector(forward:)];
640     [o_bwd_btn setAction:@selector(backward:)];
641 }
642
643 - (void)removeJumpButtons:(BOOL)b_fast
644 {
645     if (!o_prev_btn || !o_next_btn)
646         return;
647
648     if (b_fast) {
649         [o_prev_btn setHidden: YES];
650         [o_next_btn setHidden: YES];
651     } else {
652         [[o_prev_btn animator] setHidden: YES];
653         [[o_next_btn animator] setHidden: YES];
654     }
655     [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
656     [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
657     [o_prev_btn release];
658     o_prev_btn = NULL;
659     [o_next_btn release];
660     o_next_btn = NULL;
661
662     /* change the accessibility help for the backward/forward buttons accordingly */
663     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
664     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
665
666     NSRect frame;
667     frame = [o_bwd_btn frame];
668     frame.size.width++;
669     [o_bwd_btn setFrame:frame];
670     frame = [o_fwd_btn frame];
671     frame.size.width++;
672     [o_fwd_btn setFrame:frame];
673
674 #define moveItem(item) \
675 frame = [item frame]; \
676 frame.origin.x = frame.origin.x - f_space; \
677 if (b_fast) \
678     [item setFrame: frame]; \
679 else \
680     [[item animator] setFrame: frame]
681
682     float f_space = 29.;
683     moveItem(o_bwd_btn);
684     f_space = 28.;
685     moveItem(o_play_btn);
686     moveItem(o_fwd_btn);
687     f_space = 28. * 2;
688     moveItem(o_stop_btn);
689     moveItem(o_playlist_btn);
690     moveItem(o_repeat_btn);
691     moveItem(o_shuffle_btn);
692 #undef moveItem
693
694     frame = [o_progress_view frame];
695     frame.size.width = frame.size.width + f_space;
696     frame.origin.x = frame.origin.x - f_space;
697     if (b_fast)
698         [o_progress_view setFrame: frame];
699     else
700         [[o_progress_view animator] setFrame: frame];
701
702     if (b_dark_interface) {
703         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
704         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
705         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
706         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
707     } else {
708         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
709         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
710         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
711         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
712     }
713
714     [o_bottombar_view setNeedsDisplay:YES];
715
716     [o_fwd_btn setAction:@selector(fwd:)];
717     [o_bwd_btn setAction:@selector(bwd:)];
718 }
719
720 - (void)togglePlaymodeButtons
721 {
722     b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
723
724     if (b_show_playmode_buttons)
725         [self addPlaymodeButtons:NO];
726     else
727         [self removePlaymodeButtons:NO];
728 }
729
730 - (void)addPlaymodeButtons:(BOOL)b_fast
731 {
732     NSRect frame;
733     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
734
735     if (b_dark_interface) {
736         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
737         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
738     } else {
739         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
740         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
741     }
742     frame = [o_playlist_btn frame];
743     frame.size.width--;
744     [o_playlist_btn setFrame:frame];
745
746     if (b_fast) {
747         [o_repeat_btn setHidden: NO];
748         [o_shuffle_btn setHidden: NO];
749     } else {
750         [[o_repeat_btn animator] setHidden: NO];
751         [[o_shuffle_btn animator] setHidden: NO];
752     }
753
754     frame = [o_progress_view frame];
755     frame.size.width = frame.size.width - f_space;
756     frame.origin.x = frame.origin.x + f_space;
757     if (b_fast)
758         [o_progress_view setFrame: frame];
759     else
760         [[o_progress_view animator] setFrame: frame];
761 }
762
763 - (void)removePlaymodeButtons:(BOOL)b_fast
764 {
765     NSRect frame;
766     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
767     [o_repeat_btn setHidden: YES];
768     [o_shuffle_btn setHidden: YES];
769
770     if (b_dark_interface) {
771         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
772         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
773     } else {
774         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
775         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
776     }
777     frame = [o_playlist_btn frame];
778     frame.size.width++;
779     [o_playlist_btn setFrame:frame];
780
781     frame = [o_progress_view frame];
782     frame.size.width = frame.size.width + f_space;
783     frame.origin.x = frame.origin.x - f_space;
784     if (b_fast)
785         [o_progress_view setFrame: frame];
786     else
787         [[o_progress_view animator] setFrame: frame];
788 }
789
790 #pragma mark -
791 #pragma mark Extra button actions
792
793 - (IBAction)stop:(id)sender
794 {
795     [[VLCCoreInteraction sharedInstance] stop];
796 }
797
798 // dynamically created next / prev buttons
799 - (IBAction)prev:(id)sender
800 {
801     [[VLCCoreInteraction sharedInstance] previous];
802 }
803
804 - (IBAction)next:(id)sender
805 {
806     [[VLCCoreInteraction sharedInstance] next];
807 }
808
809 // alternative actions for forward / backward buttons when next / prev are activated
810 - (IBAction)forward:(id)sender
811 {
812     [[VLCCoreInteraction sharedInstance] forwardExtraShort];
813 }
814
815 - (IBAction)backward:(id)sender
816 {
817     [[VLCCoreInteraction sharedInstance] backwardExtraShort];
818 }
819
820 - (void)setRepeatOne
821 {
822     [o_repeat_btn setImage: o_repeat_one_img];
823     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
824 }
825
826 - (void)setRepeatAll
827 {
828     [o_repeat_btn setImage: o_repeat_all_img];
829     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
830 }
831
832 - (void)setRepeatOff
833 {
834     [o_repeat_btn setImage: o_repeat_img];
835     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
836 }
837
838 - (IBAction)repeat:(id)sender
839 {
840     vlc_value_t looping,repeating;
841     intf_thread_t * p_intf = VLCIntf;
842     playlist_t * p_playlist = pl_Get(p_intf);
843
844     var_Get(p_playlist, "repeat", &repeating);
845     var_Get(p_playlist, "loop", &looping);
846
847     if (!repeating.b_bool && !looping.b_bool) {
848         /* was: no repeating at all, switching to Repeat One */
849         [[VLCCoreInteraction sharedInstance] repeatOne];
850         [self setRepeatOne];
851     }
852     else if (repeating.b_bool && !looping.b_bool) {
853         /* was: Repeat One, switching to Repeat All */
854         [[VLCCoreInteraction sharedInstance] repeatAll];
855         [self setRepeatAll];
856     } else {
857         /* was: Repeat All or bug in VLC, switching to Repeat Off */
858         [[VLCCoreInteraction sharedInstance] repeatOff];
859         [self setRepeatOff];
860     }
861 }
862
863 - (void)setShuffle
864 {
865     bool b_value;
866     playlist_t *p_playlist = pl_Get(VLCIntf);
867     b_value = var_GetBool(p_playlist, "random");
868
869     if (b_value) {
870         [o_shuffle_btn setImage: o_shuffle_on_img];
871         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
872     } else {
873         [o_shuffle_btn setImage: o_shuffle_img];
874         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
875     }
876 }
877
878 - (IBAction)shuffle:(id)sender
879 {
880     [[VLCCoreInteraction sharedInstance] shuffle];
881     [self setShuffle];
882 }
883
884 - (IBAction)volumeAction:(id)sender
885 {
886     if (sender == o_volume_sld)
887         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
888     else if (sender == o_volume_down_btn)
889         [[VLCCoreInteraction sharedInstance] toggleMute];
890     else
891         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
892 }
893
894 - (IBAction)effects:(id)sender
895 {
896     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
897 }
898
899 #pragma mark -
900 #pragma mark Extra updaters
901
902 - (void)updateVolumeSlider
903 {
904     int i_volume = [[VLCCoreInteraction sharedInstance] volume];
905     BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
906
907     if (!b_muted)
908         [o_volume_sld setIntValue: i_volume];
909     else
910         [o_volume_sld setIntValue: 0];
911
912     [o_volume_sld setEnabled: !b_muted];
913     [o_volume_up_btn setEnabled: !b_muted];
914 }
915
916 - (void)updateControls
917 {
918     [super updateControls];
919
920     bool b_input = false;
921     bool b_plmul = false;
922     bool b_control = false;
923     bool b_seekable = false;
924     bool b_chapters = false;
925
926     playlist_t * p_playlist = pl_Get(VLCIntf);
927
928     PL_LOCK;
929     b_plmul = playlist_CurrentSize(p_playlist) > 1;
930     PL_UNLOCK;
931
932     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
933
934     bool b_buffering = NO;
935
936     if ((b_input = (p_input != NULL))) {
937         /* seekable streams */
938         input_state_e inputState = input_GetState(p_input);
939         if (inputState == INIT_S || inputState == OPENING_S)
940             b_buffering = YES;
941
942         /* seekable streams */
943         b_seekable = var_GetBool(p_input, "can-seek");
944
945         /* check whether slow/fast motion is possible */
946         b_control = var_GetBool(p_input, "can-rate");
947
948         /* chapters & titles */
949         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
950
951         vlc_object_release(p_input);
952     }
953
954     if (b_buffering) {
955         [o_progress_bar startAnimation:self];
956         [o_progress_bar setIndeterminate:YES];
957         [o_progress_bar setHidden:NO];
958     } else {
959         [o_progress_bar stopAnimation:self];
960         [o_progress_bar setHidden:YES];
961     }
962
963     [o_stop_btn setEnabled: b_input];
964
965     if (b_show_jump_buttons) {
966         [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
967         [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
968     }
969
970     [o_time_sld setEnabled: b_seekable];
971     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
972 }
973
974 - (void)updatePosAndTimeInFSPanel:(VLCFSPanel *)o_fspanel
975 {
976     [o_fspanel setStreamPos:[o_time_sld floatValue] andTime: [o_time_fld stringValue]];
977 }
978
979 @end