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