]> git.sesse.net Git - vlc/blob - modules/gui/macosx/ControlsBar.m
Qt: use DIR_SEP for BDMV and VIDEO_TS detection
[vlc] / modules / gui / macosx / ControlsBar.m
1 /*****************************************************************************
2  * ControlsBar.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012-2013 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     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];
114     else
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"];
121
122     // prepare time slider fance gradient view
123     if (!b_dark_interface) {
124         NSRect frame;
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];
129     }
130
131     NSRect frame;
132     frame = [o_time_sld_fancygradient_view frame];
133     frame.size.width = 0;
134     [o_time_sld_fancygradient_view setFrame: frame];
135
136     // hide resize view if necessary
137     if (!OSX_SNOW_LEOPARD)
138         [o_resize_view setImage: NULL];
139
140     if ([[o_bottombar_view window] styleMask] & NSResizableWindowMask)
141         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
142
143
144     // remove fullscreen button for lion fullscreen
145     if (b_nativeFullscreenMode) {
146         float f_width = [o_fullscreen_btn frame].size.width;
147
148         NSRect frame = [o_time_fld frame];
149         frame.origin.x += f_width;
150         [o_time_fld setFrame: frame];
151
152         frame = [o_progress_view frame];
153         frame.size.width = f_width + frame.size.width;
154         [o_progress_view setFrame: frame];
155
156         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
157     }
158
159     if (config_GetInt(VLCIntf, "macosx-show-playback-buttons"))
160         [self toggleForwardBackwardMode: YES];
161
162 }
163
164 - (CGFloat)height
165 {
166     return [o_bottombar_view frame].size.height;
167 }
168
169 - (void)toggleForwardBackwardMode:(BOOL)b_alt
170 {
171     if (b_alt == YES) {
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];
175
176         [o_fwd_btn setAction:@selector(alternateForward:)];
177         [o_bwd_btn setAction:@selector(alternateBackward:)];
178
179     } else {
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];
183
184         [o_fwd_btn setAction:@selector(fwd:)];
185         [o_bwd_btn setAction:@selector(bwd:)];
186     }
187 }
188
189 #pragma mark -
190 #pragma mark Button Actions
191
192 - (IBAction)play:(id)sender
193 {
194     [[VLCCoreInteraction sharedInstance] playOrPause];
195 }
196
197 - (void)resetPreviousButton
198 {
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;
203     }
204 }
205
206 - (void)resetBackwardSkip
207 {
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;
211 }
212
213 - (IBAction)bwd:(id)sender
214 {
215     if (!just_triggered_previous) {
216         just_triggered_previous = YES;
217         [self performSelector:@selector(resetPreviousButton)
218                    withObject: NULL
219                    afterDelay:0.40];
220     } else {
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)
226                        withObject: NULL
227                        afterDelay:0.40];
228         }
229     }
230 }
231
232 - (void)resetNextButton
233 {
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;
238     }
239 }
240
241 - (void)resetForwardSkip
242 {
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;
246 }
247
248 - (IBAction)fwd:(id)sender
249 {
250     if (!just_triggered_next) {
251         just_triggered_next = YES;
252         [self performSelector:@selector(resetNextButton)
253                    withObject: NULL
254                    afterDelay:0.40];
255     } else {
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)
261                        withObject: NULL
262                        afterDelay:0.40];
263         }
264     }
265 }
266
267 // alternative actions for forward / backward buttons when next / prev are activated
268 - (IBAction)alternateForward:(id)sender
269 {
270     [[VLCCoreInteraction sharedInstance] forwardExtraShort];
271 }
272
273 - (IBAction)alternateBackward:(id)sender
274 {
275     [[VLCCoreInteraction sharedInstance] backwardExtraShort];
276 }
277
278 - (IBAction)timeSliderAction:(id)sender
279 {
280     float f_updated;
281     input_thread_t * p_input;
282
283     switch([[NSApp currentEvent] type]) {
284         case NSLeftMouseUp:
285         case NSLeftMouseDown:
286         case NSLeftMouseDragged:
287             f_updated = [sender floatValue];
288             break;
289
290         default:
291             return;
292     }
293     p_input = pl_CurrentInput(VLCIntf);
294     if (p_input != NULL) {
295         vlc_value_t pos;
296         NSString * o_time;
297
298         pos.f_float = f_updated / 10000.;
299         var_Set(p_input, "position", pos);
300         [o_time_sld setFloatValue: f_updated];
301
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);
305     }
306 }
307
308 - (IBAction)fullscreen:(id)sender
309 {
310     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
311 }
312
313 #pragma mark -
314 #pragma mark Updaters
315
316 - (void)updateTimeSlider
317 {
318     input_thread_t * p_input;
319     p_input = pl_CurrentInput(VLCIntf);
320     if (p_input) {
321         NSString * o_time;
322         vlc_value_t pos;
323         float f_updated;
324
325         var_Get(p_input, "position", &pos);
326         f_updated = 10000. * pos.f_float;
327         [o_time_sld setFloatValue: f_updated];
328
329         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
330
331         mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
332         if (dur == -1) {
333             [o_time_sld setHidden: YES];
334             [o_time_sld_fancygradient_view setHidden: YES];
335         } else {
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)
340                     b_buffering = YES;
341
342                 [o_time_sld setHidden: b_buffering];
343                 [o_time_sld_fancygradient_view setHidden: b_buffering];
344             }
345         }
346         [o_time_fld setStringValue: o_time];
347         [o_time_fld setNeedsDisplay:YES];
348
349         vlc_object_release(p_input);
350     } else {
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];
355     }
356 }
357
358 - (void)drawFancyGradientEffectForTimeSlider
359 {
360     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
361     CGFloat f_value = [o_time_sld knobPosition];
362     if (f_value > 7.5) {
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)];
368         }
369     } else {
370         NSRect frame;
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];
375         }
376         [o_time_sld_fancygradient_view setHidden: YES];
377     }
378     [o_pool release];
379 }
380
381 - (void)updateControls
382 {
383     bool b_plmul = false;
384     bool b_seekable = false;
385     bool b_chapters = false;
386     bool b_buffering = false;
387
388     playlist_t * p_playlist = pl_Get(VLCIntf);
389
390     PL_LOCK;
391     b_plmul = playlist_CurrentSize(p_playlist) > 1;
392     PL_UNLOCK;
393
394     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
395
396
397     if (p_input) {
398         input_state_e inputState = input_GetState(p_input);
399         if (inputState == INIT_S || inputState == OPENING_S)
400             b_buffering = YES;
401
402         /* seekable streams */
403         b_seekable = var_GetBool(p_input, "can-seek");
404
405         /* chapters & titles */
406         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
407
408         vlc_object_release(p_input);
409     }
410
411
412     if (b_buffering) {
413         [o_progress_bar startAnimation:self];
414         [o_progress_bar setIndeterminate:YES];
415         [o_progress_bar setHidden:NO];
416     } else {
417         [o_progress_bar stopAnimation:self];
418         [o_progress_bar setHidden:YES];
419     }
420
421     [o_time_sld setEnabled: b_seekable];
422
423     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
424     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
425 }
426
427 - (void)setPause
428 {
429     [o_play_btn setImage: o_pause_img];
430     [o_play_btn setAlternateImage: o_pause_pressed_img];
431     [o_play_btn setToolTip: _NS("Pause")];
432 }
433
434 - (void)setPlay
435 {
436     [o_play_btn setImage: o_play_img];
437     [o_play_btn setAlternateImage: o_play_pressed_img];
438     [o_play_btn setToolTip: _NS("Play")];
439 }
440
441 - (void)setFullscreenState:(BOOL)b_fullscreen
442 {
443     if (!b_nativeFullscreenMode)
444         [o_fullscreen_btn setState:b_fullscreen];
445 }
446
447 @end
448
449
450 /*****************************************************************************
451  * VLCMainWindowControlsBar
452  *
453  *  Holds all specific outlets, actions and code for the main window controls bar.
454  *****************************************************************************/
455
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;
461 @end
462
463 @implementation VLCMainWindowControlsBar
464
465 - (void)awakeFromNib
466 {
467     [super awakeFromNib];
468
469
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];
473
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];
477
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];
481
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];
485
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];
495
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];
499
500     if (!b_dark_interface) {
501         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
502         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
503
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];
516
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];
521
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"]];
525         } else {
526             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
527             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
528         }
529
530         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
531         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
532     } else {
533         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
534         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
535
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];
548
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];
553
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"]];
557         } else {
558             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
559             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
560         }
561
562         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
563         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
564     }
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];
569
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];
574
575     // remove fullscreen button for lion fullscreen
576     if (b_nativeFullscreenMode) {
577         NSRect frame;
578
579         // == [o_fullscreen_btn frame].size.width;
580         // button is already removed!
581         float f_width = 29.;
582 #define moveItem(item) \
583 frame = [item frame]; \
584 frame.origin.x = f_width + frame.origin.x; \
585 [item setFrame: frame]
586
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);
592 #undef moveItem
593
594         // time field and progress bar are moved in super method!
595     }
596
597
598     b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
599     if (b_show_jump_buttons)
600         [self addJumpButtons:YES];
601
602     b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
603     if (!b_show_playmode_buttons)
604         [self removePlaymodeButtons:YES];
605
606     if (!config_GetInt(VLCIntf, "macosx-show-effects-button"))
607         [self removeEffectsButton:YES];
608
609     [[VLCMain sharedInstance] playbackModeUpdated];
610
611 }
612
613 #pragma mark -
614 #pragma mark interface customization
615
616
617 - (void)toggleEffectsButton
618 {
619     if (config_GetInt(VLCIntf, "macosx-show-effects-button"))
620         [self addEffectsButton:NO];
621     else
622         [self removeEffectsButton:NO];
623 }
624
625 - (void)addEffectsButton:(BOOL)b_fast
626 {
627     if (!o_effects_btn)
628         return;
629
630     if (b_fast) {
631         [o_effects_btn setHidden: NO];
632     } else {
633         [[o_effects_btn animator] setHidden: NO];
634     }
635
636 #define moveItem(item) \
637 frame = [item frame]; \
638 frame.origin.x = frame.origin.x - f_space; \
639 if (b_fast) \
640 [item setFrame: frame]; \
641 else \
642 [[item animator] setFrame: frame]
643
644     NSRect frame;
645     float f_space = [o_effects_btn frame].size.width;
646     // extra margin between button and volume up button
647     if (b_nativeFullscreenMode)
648         f_space += 2;
649
650
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);
656 #undef moveItem
657
658
659     frame = [o_progress_view frame];
660     frame.size.width = frame.size.width - f_space;
661     if (b_fast)
662         [o_progress_view setFrame: frame];
663     else
664         [[o_progress_view animator] setFrame: frame];
665
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"]];
670         } else {
671             [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
672             [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
673         }
674     }
675
676     [o_bottombar_view setNeedsDisplay:YES];
677 }
678
679 - (void)removeEffectsButton:(BOOL)b_fast
680 {
681     if (!o_effects_btn)
682         return;
683
684     [o_effects_btn setHidden: YES];
685
686 #define moveItem(item) \
687 frame = [item frame]; \
688 frame.origin.x = frame.origin.x + f_space; \
689 if (b_fast) \
690 [item setFrame: frame]; \
691 else \
692 [[item animator] setFrame: frame]
693
694     NSRect frame;
695     float f_space = [o_effects_btn frame].size.width;
696     // extra margin between button and volume up button
697     if (b_nativeFullscreenMode)
698         f_space += 2;
699
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);
705 #undef moveItem
706
707
708     frame = [o_progress_view frame];
709     frame.size.width = frame.size.width + f_space;
710     if (b_fast)
711         [o_progress_view setFrame: frame];
712     else
713         [[o_progress_view animator] setFrame: frame];
714
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"]];
719         } else {
720             [[o_fullscreen_btn animator] setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
721             [[o_fullscreen_btn animator] setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
722         }
723     }
724
725     [o_bottombar_view setNeedsDisplay:YES];
726 }
727
728 - (void)toggleJumpButtons
729 {
730     b_show_jump_buttons = config_GetInt(VLCIntf, "macosx-show-playback-buttons");
731
732     if (b_show_jump_buttons)
733         [self addJumpButtons:NO];
734     else
735         [self removeJumpButtons:NO];
736 }
737
738 - (void)addJumpButtons:(BOOL)b_fast
739 {
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];
753
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];
764
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"]];
770     } else {
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"]];
775     }
776
777     NSRect frame;
778     frame = [o_bwd_btn frame];
779     frame.size.width--;
780     [o_bwd_btn setFrame:frame];
781     frame = [o_fwd_btn frame];
782     frame.size.width--;
783     [o_fwd_btn setFrame:frame];
784
785 #define moveItem(item) \
786 frame = [item frame]; \
787 frame.origin.x = frame.origin.x + f_space; \
788 if (b_fast) \
789     [item setFrame: frame]; \
790 else \
791     [[item animator] setFrame: frame]
792
793     float f_space = 29.;
794     moveItem(o_bwd_btn);
795     f_space = 28.;
796     moveItem(o_play_btn);
797     moveItem(o_fwd_btn);
798     f_space = 28. * 2;
799     moveItem(o_stop_btn);
800     moveItem(o_playlist_btn);
801     moveItem(o_repeat_btn);
802     moveItem(o_shuffle_btn);
803 #undef moveItem
804
805     frame = [o_progress_view frame];
806     frame.size.width = frame.size.width - f_space;
807     frame.origin.x = frame.origin.x + f_space;
808     if (b_fast)
809         [o_progress_view setFrame: frame];
810     else
811         [[o_progress_view animator] setFrame: frame];
812
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"]];
818     } else {
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"]];
823     }
824
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];
827
828     // wait until the animation is done, if displayed
829     if (b_fast) {
830         [o_bottombar_view addSubview:o_prev_btn];
831         [o_bottombar_view addSubview:o_next_btn];
832     } else {
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];
835     }
836
837     [self toggleForwardBackwardMode: YES];
838 }
839
840 - (void)removeJumpButtons:(BOOL)b_fast
841 {
842     if (!o_prev_btn || !o_next_btn)
843         return;
844
845     if (b_fast) {
846         [o_prev_btn setHidden: YES];
847         [o_next_btn setHidden: YES];
848     } else {
849         [[o_prev_btn animator] setHidden: YES];
850         [[o_next_btn animator] setHidden: YES];
851     }
852     [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
853     [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
854     [o_prev_btn release];
855     o_prev_btn = NULL;
856     [o_next_btn release];
857     o_next_btn = NULL;
858
859     NSRect frame;
860     frame = [o_bwd_btn frame];
861     frame.size.width++;
862     [o_bwd_btn setFrame:frame];
863     frame = [o_fwd_btn frame];
864     frame.size.width++;
865     [o_fwd_btn setFrame:frame];
866
867 #define moveItem(item) \
868 frame = [item frame]; \
869 frame.origin.x = frame.origin.x - f_space; \
870 if (b_fast) \
871     [item setFrame: frame]; \
872 else \
873     [[item animator] setFrame: frame]
874
875     float f_space = 29.;
876     moveItem(o_bwd_btn);
877     f_space = 28.;
878     moveItem(o_play_btn);
879     moveItem(o_fwd_btn);
880     f_space = 28. * 2;
881     moveItem(o_stop_btn);
882     moveItem(o_playlist_btn);
883     moveItem(o_repeat_btn);
884     moveItem(o_shuffle_btn);
885 #undef moveItem
886
887     frame = [o_progress_view frame];
888     frame.size.width = frame.size.width + f_space;
889     frame.origin.x = frame.origin.x - f_space;
890     if (b_fast)
891         [o_progress_view setFrame: frame];
892     else
893         [[o_progress_view animator] setFrame: frame];
894
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"]];
900     } else {
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"]];
905     }
906
907     [self toggleForwardBackwardMode: NO];
908
909     [o_bottombar_view setNeedsDisplay:YES];
910 }
911
912 - (void)togglePlaymodeButtons
913 {
914     b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
915
916     if (b_show_playmode_buttons)
917         [self addPlaymodeButtons:NO];
918     else
919         [self removePlaymodeButtons:NO];
920 }
921
922 - (void)addPlaymodeButtons:(BOOL)b_fast
923 {
924     NSRect frame;
925     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
926
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"]];
930     } else {
931         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
932         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
933     }
934     frame = [o_playlist_btn frame];
935     frame.size.width--;
936     [o_playlist_btn setFrame:frame];
937
938     if (b_fast) {
939         [o_repeat_btn setHidden: NO];
940         [o_shuffle_btn setHidden: NO];
941     } else {
942         [[o_repeat_btn animator] setHidden: NO];
943         [[o_shuffle_btn animator] setHidden: NO];
944     }
945
946     frame = [o_progress_view frame];
947     frame.size.width = frame.size.width - f_space;
948     frame.origin.x = frame.origin.x + f_space;
949     if (b_fast)
950         [o_progress_view setFrame: frame];
951     else
952         [[o_progress_view animator] setFrame: frame];
953 }
954
955 - (void)removePlaymodeButtons:(BOOL)b_fast
956 {
957     NSRect frame;
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];
961
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"]];
965     } else {
966         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
967         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
968     }
969     frame = [o_playlist_btn frame];
970     frame.size.width++;
971     [o_playlist_btn setFrame:frame];
972
973     frame = [o_progress_view frame];
974     frame.size.width = frame.size.width + f_space;
975     frame.origin.x = frame.origin.x - f_space;
976     if (b_fast)
977         [o_progress_view setFrame: frame];
978     else
979         [[o_progress_view animator] setFrame: frame];
980 }
981
982 #pragma mark -
983 #pragma mark Extra button actions
984
985 - (IBAction)stop:(id)sender
986 {
987     [[VLCCoreInteraction sharedInstance] stop];
988 }
989
990 // dynamically created next / prev buttons
991 - (IBAction)prev:(id)sender
992 {
993     [[VLCCoreInteraction sharedInstance] previous];
994 }
995
996 - (IBAction)next:(id)sender
997 {
998     [[VLCCoreInteraction sharedInstance] next];
999 }
1000
1001 - (void)setRepeatOne
1002 {
1003     [o_repeat_btn setImage: o_repeat_one_img];
1004     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1005 }
1006
1007 - (void)setRepeatAll
1008 {
1009     [o_repeat_btn setImage: o_repeat_all_img];
1010     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1011 }
1012
1013 - (void)setRepeatOff
1014 {
1015     [o_repeat_btn setImage: o_repeat_img];
1016     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1017 }
1018
1019 - (IBAction)repeat:(id)sender
1020 {
1021     vlc_value_t looping,repeating;
1022     intf_thread_t * p_intf = VLCIntf;
1023     playlist_t * p_playlist = pl_Get(p_intf);
1024
1025     var_Get(p_playlist, "repeat", &repeating);
1026     var_Get(p_playlist, "loop", &looping);
1027
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];
1032     }
1033     else if (repeating.b_bool && !looping.b_bool) {
1034         /* was: Repeat One, switching to Repeat All */
1035         [[VLCCoreInteraction sharedInstance] repeatAll];
1036         [self setRepeatAll];
1037     } else {
1038         /* was: Repeat All or bug in VLC, switching to Repeat Off */
1039         [[VLCCoreInteraction sharedInstance] repeatOff];
1040         [self setRepeatOff];
1041     }
1042 }
1043
1044 - (void)setShuffle
1045 {
1046     bool b_value;
1047     playlist_t *p_playlist = pl_Get(VLCIntf);
1048     b_value = var_GetBool(p_playlist, "random");
1049
1050     if (b_value) {
1051         [o_shuffle_btn setImage: o_shuffle_on_img];
1052         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1053     } else {
1054         [o_shuffle_btn setImage: o_shuffle_img];
1055         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1056     }
1057 }
1058
1059 - (IBAction)shuffle:(id)sender
1060 {
1061     [[VLCCoreInteraction sharedInstance] shuffle];
1062     [self setShuffle];
1063 }
1064
1065 - (IBAction)togglePlaylist:(id)sender
1066 {
1067     [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserEvent];
1068 }
1069
1070 - (IBAction)volumeAction:(id)sender
1071 {
1072     if (sender == o_volume_sld)
1073         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1074     else if (sender == o_volume_down_btn)
1075         [[VLCCoreInteraction sharedInstance] toggleMute];
1076     else
1077         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1078 }
1079
1080 - (IBAction)effects:(id)sender
1081 {
1082     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1083 }
1084
1085 #pragma mark -
1086 #pragma mark Extra updaters
1087
1088 - (void)updateVolumeSlider
1089 {
1090     int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1091     BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1092
1093     if (!b_muted)
1094         [o_volume_sld setIntValue: i_volume];
1095     else
1096         [o_volume_sld setIntValue: 0];
1097
1098     [o_volume_sld setEnabled: !b_muted];
1099     [o_volume_up_btn setEnabled: !b_muted];
1100 }
1101
1102 - (void)updateControls
1103 {
1104     [super updateControls];
1105
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;
1111
1112     playlist_t * p_playlist = pl_Get(VLCIntf);
1113
1114     PL_LOCK;
1115     b_plmul = playlist_CurrentSize(p_playlist) > 1;
1116     PL_UNLOCK;
1117
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");
1122
1123         /* check whether slow/fast motion is possible */
1124         b_control = var_GetBool(p_input, "can-rate");
1125
1126         /* chapters & titles */
1127         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1128
1129         vlc_object_release(p_input);
1130     }
1131
1132     [o_stop_btn setEnabled: b_input];
1133
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)];
1137     }
1138
1139     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1140 }
1141
1142 @end