]> git.sesse.net Git - vlc/blob - modules/gui/macosx/ControlsBar.m
macosx/CAS: add MKV to the black list for HTTP streaming
[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     float f_space = 29.;
588 #define moveItem(item) \
589 frame = [item frame]; \
590 frame.origin.x = frame.origin.x + f_space; \
591 if (b_fast) \
592 [item setFrame: frame]; \
593 else \
594 [[item animator] setFrame: frame]
595
596     moveItem(o_bwd_btn);
597     moveItem(o_play_btn);
598     f_space = 28.;
599     moveItem(o_fwd_btn);
600     f_space = 57.;
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_next_btn frame].origin.x + 82. + [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_next_btn release];
659
660     /* change the accessibility help for the backward/forward buttons accordingly */
661     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
662     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
663
664     NSRect frame;
665     frame = [o_bwd_btn frame];
666     frame.size.width--;
667     [o_bwd_btn setFrame:frame];
668     frame = [o_fwd_btn frame];
669     frame.size.width--;
670     [o_fwd_btn setFrame:frame];
671
672     float f_space = 29.;
673 #define moveItem(item) \
674 frame = [item frame]; \
675 frame.origin.x = frame.origin.x - f_space; \
676 if (b_fast) \
677 [item setFrame: frame]; \
678 else \
679 [[item animator] setFrame: frame]
680
681     moveItem(o_bwd_btn);
682     moveItem(o_play_btn);
683     f_space = 28.;
684     moveItem(o_fwd_btn);
685     f_space = 57.;
686     moveItem(o_stop_btn);
687     moveItem(o_playlist_btn);
688     moveItem(o_repeat_btn);
689     moveItem(o_shuffle_btn);
690 #undef moveItem
691
692     frame = [o_progress_view frame];
693     frame.size.width = frame.size.width + f_space;
694     frame.origin.x = frame.origin.x - f_space;
695     if (b_fast)
696         [o_progress_view setFrame: frame];
697     else
698         [[o_progress_view animator] setFrame: frame];
699
700     if (b_dark_interface) {
701         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
702         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
703         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
704         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
705     } else {
706         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
707         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
708         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
709         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
710     }
711
712     [o_bottombar_view setNeedsDisplay:YES];
713
714     [o_fwd_btn setAction:@selector(fwd:)];
715     [o_bwd_btn setAction:@selector(bwd:)];
716 }
717
718 - (void)togglePlaymodeButtons
719 {
720     b_show_playmode_buttons = config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
721
722     if (b_show_playmode_buttons)
723         [self addPlaymodeButtons:NO];
724     else
725         [self removePlaymodeButtons:NO];
726 }
727
728 - (void)addPlaymodeButtons:(BOOL)b_fast
729 {
730     NSRect frame;
731     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
732
733     if (b_dark_interface) {
734         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
735         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
736     } else {
737         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
738         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
739     }
740     frame = [o_playlist_btn frame];
741     frame.size.width--;
742     [o_playlist_btn setFrame:frame];
743
744     if (b_fast) {
745         [o_repeat_btn setHidden: NO];
746         [o_shuffle_btn setHidden: NO];
747     } else {
748         [[o_repeat_btn animator] setHidden: NO];
749         [[o_shuffle_btn animator] setHidden: NO];
750     }
751
752     frame = [o_progress_view frame];
753     frame.size.width = frame.size.width - f_space;
754     frame.origin.x = frame.origin.x + f_space;
755     if (b_fast)
756         [o_progress_view setFrame: frame];
757     else
758         [[o_progress_view animator] setFrame: frame];
759 }
760
761 - (void)removePlaymodeButtons:(BOOL)b_fast
762 {
763     NSRect frame;
764     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
765     [o_repeat_btn setHidden: YES];
766     [o_shuffle_btn setHidden: YES];
767
768     if (b_dark_interface) {
769         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
770         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
771     } else {
772         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
773         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
774     }
775     frame = [o_playlist_btn frame];
776     frame.size.width++;
777     [o_playlist_btn setFrame:frame];
778
779     frame = [o_progress_view frame];
780     frame.size.width = frame.size.width + f_space;
781     frame.origin.x = frame.origin.x - f_space;
782     if (b_fast)
783         [o_progress_view setFrame: frame];
784     else
785         [[o_progress_view animator] setFrame: frame];
786 }
787
788 #pragma mark -
789 #pragma mark Extra button actions
790
791 - (IBAction)stop:(id)sender
792 {
793     [[VLCCoreInteraction sharedInstance] stop];
794 }
795
796 // dynamically created next / prev buttons
797 - (IBAction)prev:(id)sender
798 {
799     [[VLCCoreInteraction sharedInstance] previous];
800 }
801
802 - (IBAction)next:(id)sender
803 {
804     [[VLCCoreInteraction sharedInstance] next];
805 }
806
807
808 - (void)setRepeatOne
809 {
810     [o_repeat_btn setImage: o_repeat_one_img];
811     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
812 }
813
814 - (void)setRepeatAll
815 {
816     [o_repeat_btn setImage: o_repeat_all_img];
817     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
818 }
819
820 - (void)setRepeatOff
821 {
822     [o_repeat_btn setImage: o_repeat_img];
823     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
824 }
825
826 - (IBAction)repeat:(id)sender
827 {
828     vlc_value_t looping,repeating;
829     intf_thread_t * p_intf = VLCIntf;
830     playlist_t * p_playlist = pl_Get(p_intf);
831
832     var_Get(p_playlist, "repeat", &repeating);
833     var_Get(p_playlist, "loop", &looping);
834
835     if (!repeating.b_bool && !looping.b_bool) {
836         /* was: no repeating at all, switching to Repeat One */
837         [[VLCCoreInteraction sharedInstance] repeatOne];
838         [self setRepeatOne];
839     }
840     else if (repeating.b_bool && !looping.b_bool) {
841         /* was: Repeat One, switching to Repeat All */
842         [[VLCCoreInteraction sharedInstance] repeatAll];
843         [self setRepeatAll];
844     } else {
845         /* was: Repeat All or bug in VLC, switching to Repeat Off */
846         [[VLCCoreInteraction sharedInstance] repeatOff];
847         [self setRepeatOff];
848     }
849 }
850
851 - (void)setShuffle
852 {
853     bool b_value;
854     playlist_t *p_playlist = pl_Get(VLCIntf);
855     b_value = var_GetBool(p_playlist, "random");
856
857     if (b_value) {
858         [o_shuffle_btn setImage: o_shuffle_on_img];
859         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
860     } else {
861         [o_shuffle_btn setImage: o_shuffle_img];
862         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
863     }
864 }
865
866 - (IBAction)shuffle:(id)sender
867 {
868     [[VLCCoreInteraction sharedInstance] shuffle];
869     [self setShuffle];
870 }
871
872 - (IBAction)volumeAction:(id)sender
873 {
874     if (sender == o_volume_sld)
875         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
876     else if (sender == o_volume_down_btn)
877         [[VLCCoreInteraction sharedInstance] toggleMute];
878     else
879         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
880 }
881
882 - (IBAction)effects:(id)sender
883 {
884     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
885 }
886
887 #pragma mark -
888 #pragma mark Extra updaters
889
890 - (void)updateVolumeSlider
891 {
892     int i_volume = [[VLCCoreInteraction sharedInstance] volume];
893     BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
894
895     if (!b_muted)
896         [o_volume_sld setIntValue: i_volume];
897     else
898         [o_volume_sld setIntValue: 0];
899
900     [o_volume_sld setEnabled: !b_muted];
901     [o_volume_up_btn setEnabled: !b_muted];
902 }
903
904 - (void)updateControls
905 {
906     [super updateControls];
907
908     bool b_input = false;
909     bool b_plmul = false;
910     bool b_control = false;
911     bool b_seekable = false;
912     bool b_chapters = false;
913
914     playlist_t * p_playlist = pl_Get(VLCIntf);
915
916     PL_LOCK;
917     b_plmul = playlist_CurrentSize(p_playlist) > 1;
918     PL_UNLOCK;
919
920     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
921
922     bool b_buffering = NO;
923
924     if ((b_input = (p_input != NULL))) {
925         /* seekable streams */
926         input_state_e inputState = input_GetState(p_input);
927         if (inputState == INIT_S || inputState == OPENING_S)
928             b_buffering = YES;
929
930         /* seekable streams */
931         b_seekable = var_GetBool(p_input, "can-seek");
932
933         /* check whether slow/fast motion is possible */
934         b_control = var_GetBool(p_input, "can-rate");
935
936         /* chapters & titles */
937         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
938
939         vlc_object_release(p_input);
940     }
941
942     if (b_buffering) {
943         [o_progress_bar startAnimation:self];
944         [o_progress_bar setIndeterminate:YES];
945         [o_progress_bar setHidden:NO];
946     } else {
947         [o_progress_bar stopAnimation:self];
948         [o_progress_bar setHidden:YES];
949     }
950
951     [o_stop_btn setEnabled: b_input];
952
953     if (b_show_jump_buttons) {
954         [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
955         [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
956     }
957
958     [o_time_sld setEnabled: b_seekable];
959     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
960 }
961
962 - (void)updatePosAndTimeInFSPanel:(VLCFSPanel *)o_fspanel
963 {
964     [o_fspanel setStreamPos:[o_time_sld floatValue] andTime: [o_time_fld stringValue]];
965 }
966
967 @end