macosx: Add missing super awakeFromNib call
[vlc.git] / modules / gui / macosx / VLCControlsBarCommon.m
1 /*****************************************************************************
2  * VLCControlsBarCommon.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012-2016 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 "VLCControlsBarCommon.h"
26 #import "VLCMain.h"
27 #import "VLCCoreInteraction.h"
28 #import "VLCMainMenu.h"
29 #import "VLCPlaylist.h"
30 #import "CompatibilityFixes.h"
31
32 /*****************************************************************************
33  * VLCControlsBarCommon
34  *
35  *  Holds all outlets, actions and code common for controls bar in detached
36  *  and in main window.
37  *****************************************************************************/
38
39 @interface VLCControlsBarCommon ()
40 {
41     NSImage * _pauseImage;
42     NSImage * _pressedPauseImage;
43     NSImage * _playImage;
44     NSImage * _pressedPlayImage;
45
46     NSTimeInterval last_fwd_event;
47     NSTimeInterval last_bwd_event;
48     BOOL just_triggered_next;
49     BOOL just_triggered_previous;
50 }
51 @end
52
53 @implementation VLCControlsBarCommon
54
55 - (void)awakeFromNib
56 {
57     [super awakeFromNib];
58     
59     _darkInterface = var_InheritBool(getIntf(), "macosx-interfacestyle");
60     _nativeFullscreenMode = var_InheritBool(getIntf(), "macosx-nativefullscreenmode");
61
62     [self.dropView setDrawBorder: NO];
63
64     [self.playButton setToolTip: _NS("Play/Pause")];
65     [[self.playButton cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
66     [[self.playButton cell] accessibilitySetOverrideValue:[self.playButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
67
68     [self.backwardButton setToolTip: _NS("Backward")];
69     [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
70     [[self.backwardButton cell] accessibilitySetOverrideValue:[self.backwardButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
71
72     [self.forwardButton setToolTip: _NS("Forward")];
73     [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
74     [[self.forwardButton cell] accessibilitySetOverrideValue:[self.forwardButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
75
76     [self.timeSlider setToolTip: _NS("Position")];
77     [[self.timeSlider cell] accessibilitySetOverrideValue:_NS("Click and move the mouse while keeping the button pressed to use this slider to change current playback position.") forAttribute:NSAccessibilityDescriptionAttribute];
78     [[self.timeSlider cell] accessibilitySetOverrideValue:[self.timeSlider toolTip] forAttribute:NSAccessibilityTitleAttribute];
79     if (_darkInterface)
80         [self.timeSlider setSliderStyleDark];
81
82     [self.fullscreenButton setToolTip: _NS("Toggle Fullscreen mode")];
83     [[self.fullscreenButton cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
84     [[self.fullscreenButton cell] accessibilitySetOverrideValue:[self.fullscreenButton toolTip] forAttribute:NSAccessibilityTitleAttribute];
85
86     if (!_darkInterface) {
87         [self.bottomBarView setImagesLeft: imageFromRes(@"bottom-background") middle: imageFromRes(@"bottom-background") right: imageFromRes(@"bottom-background")];
88
89         [self.backwardButton setImage: imageFromRes(@"backward-3btns")];
90         [self.backwardButton setAlternateImage: imageFromRes(@"backward-3btns-pressed")];
91         _playImage = imageFromRes(@"play");
92         _pressedPlayImage = imageFromRes(@"play-pressed");
93         _pauseImage = imageFromRes(@"pause");
94         _pressedPauseImage = imageFromRes(@"pause-pressed");
95         [self.forwardButton setImage: imageFromRes(@"forward-3btns")];
96         [self.forwardButton setAlternateImage: imageFromRes(@"forward-3btns-pressed")];
97
98         [self.fullscreenButton setImage: imageFromRes(@"fullscreen-one-button")];
99         [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed")];
100     } else {
101         [self.bottomBarView setImagesLeft: imageFromRes(@"bottomdark-left") middle: imageFromRes(@"bottom-background_dark") right: imageFromRes(@"bottomdark-right")];
102
103         [self.backwardButton setImage: imageFromRes(@"backward-3btns-dark")];
104         [self.backwardButton setAlternateImage: imageFromRes(@"backward-3btns-dark-pressed")];
105         _playImage = imageFromRes(@"play_dark");
106         _pressedPlayImage = imageFromRes(@"play-pressed_dark");
107         _pauseImage = imageFromRes(@"pause_dark");
108         _pressedPauseImage = imageFromRes(@"pause-pressed_dark");
109         [self.forwardButton setImage: imageFromRes(@"forward-3btns-dark")];
110         [self.forwardButton setAlternateImage: imageFromRes(@"forward-3btns-dark-pressed")];
111
112         [self.fullscreenButton setImage: imageFromRes(@"fullscreen-one-button-pressed_dark")];
113         [self.fullscreenButton setAlternateImage: imageFromRes(@"fullscreen-one-button-pressed_dark")];
114     }
115
116     [self.playButton setImage: _playImage];
117     [self.playButton setAlternateImage: _pressedPlayImage];
118
119     NSColor *timeFieldTextColor;
120     if (!var_InheritBool(getIntf(), "macosx-interfacestyle"))
121         timeFieldTextColor = [NSColor colorWithCalibratedRed:0.229 green:0.229 blue:0.229 alpha:100.0];
122     else
123         timeFieldTextColor = [NSColor colorWithCalibratedRed:0.64 green:0.64 blue:0.64 alpha:100.0];
124     [self.timeField setTextColor: timeFieldTextColor];
125     [self.timeField setFont:[NSFont titleBarFontOfSize:10.0]];
126     [self.timeField setAlignment: NSCenterTextAlignment];
127     [self.timeField setNeedsDisplay:YES];
128     [self.timeField setRemainingIdentifier:@"DisplayTimeAsTimeRemaining"];
129
130     // remove fullscreen button for lion fullscreen
131     if (_nativeFullscreenMode) {
132         [self.fullscreenButton removeFromSuperview];
133     }
134
135     if (config_GetInt(getIntf(), "macosx-show-playback-buttons"))
136         [self toggleForwardBackwardMode: YES];
137
138 }
139
140 - (CGFloat)height
141 {
142     return [self.bottomBarView frame].size.height;
143 }
144
145 - (void)toggleForwardBackwardMode:(BOOL)b_alt
146 {
147     if (b_alt == YES) {
148         /* change the accessibility help for the backward/forward buttons accordingly */
149         [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
150         [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
151
152         [self.forwardButton setAction:@selector(alternateForward:)];
153         [self.backwardButton setAction:@selector(alternateBackward:)];
154
155     } else {
156         /* change the accessibility help for the backward/forward buttons accordingly */
157         [[self.backwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
158         [[self.forwardButton cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
159
160         [self.forwardButton setAction:@selector(fwd:)];
161         [self.backwardButton setAction:@selector(bwd:)];
162     }
163 }
164
165 #pragma mark -
166 #pragma mark Button Actions
167
168 - (IBAction)play:(id)sender
169 {
170     [[VLCCoreInteraction sharedInstance] playOrPause];
171 }
172
173 - (void)resetPreviousButton
174 {
175     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
176         // seems like no further event occurred, so let's switch the playback item
177         [[VLCCoreInteraction sharedInstance] previous];
178         just_triggered_previous = NO;
179     }
180 }
181
182 - (void)resetBackwardSkip
183 {
184     // the user stopped skipping, so let's allow him to change the item
185     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
186         just_triggered_previous = NO;
187 }
188
189 - (IBAction)bwd:(id)sender
190 {
191     if (!just_triggered_previous) {
192         just_triggered_previous = YES;
193         [self performSelector:@selector(resetPreviousButton)
194                    withObject: NULL
195                    afterDelay:0.40];
196     } else {
197         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
198             // we just skipped 4 "continous" events, otherwise we are too fast
199             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
200             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
201             [self performSelector:@selector(resetBackwardSkip)
202                        withObject: NULL
203                        afterDelay:0.40];
204         }
205     }
206 }
207
208 - (void)resetNextButton
209 {
210     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
211         // seems like no further event occurred, so let's switch the playback item
212         [[VLCCoreInteraction sharedInstance] next];
213         just_triggered_next = NO;
214     }
215 }
216
217 - (void)resetForwardSkip
218 {
219     // the user stopped skipping, so let's allow him to change the item
220     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
221         just_triggered_next = NO;
222 }
223
224 - (IBAction)fwd:(id)sender
225 {
226     if (!just_triggered_next) {
227         just_triggered_next = YES;
228         [self performSelector:@selector(resetNextButton)
229                    withObject: NULL
230                    afterDelay:0.40];
231     } else {
232         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16) {
233             // we just skipped 4 "continous" events, otherwise we are too fast
234             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
235             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
236             [self performSelector:@selector(resetForwardSkip)
237                        withObject: NULL
238                        afterDelay:0.40];
239         }
240     }
241 }
242
243 // alternative actions for forward / backward buttons when next / prev are activated
244 - (IBAction)alternateForward:(id)sender
245 {
246     [[VLCCoreInteraction sharedInstance] forwardExtraShort];
247 }
248
249 - (IBAction)alternateBackward:(id)sender
250 {
251     [[VLCCoreInteraction sharedInstance] backwardExtraShort];
252 }
253
254 - (IBAction)timeSliderAction:(id)sender
255 {
256     float f_updated;
257     input_thread_t * p_input;
258
259     switch([[NSApp currentEvent] type]) {
260         case NSLeftMouseUp:
261             /* Ignore mouse up, as this is a continous slider and
262              * when the user does a single click to a position on the slider,
263              * the action is called twice, once for the mouse down and once
264              * for the mouse up event. This results in two short seeks one
265              * after another to the same position, which results in weird
266              * audio quirks.
267              */
268             return;
269         case NSLeftMouseDown:
270         case NSLeftMouseDragged:
271             f_updated = [sender floatValue];
272             break;
273
274         default:
275             return;
276     }
277     p_input = pl_CurrentInput(getIntf());
278     if (p_input != NULL) {
279         vlc_value_t pos;
280         NSString * o_time;
281
282         pos.f_float = f_updated / 10000.;
283         var_Set(p_input, "position", pos);
284         [self.timeSlider setFloatValue: f_updated];
285
286         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[self.timeField timeRemaining]];
287         [self.timeField setStringValue: o_time];
288         vlc_object_release(p_input);
289     }
290 }
291
292 - (IBAction)fullscreen:(id)sender
293 {
294     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
295 }
296
297 #pragma mark -
298 #pragma mark Updaters
299
300 - (void)updateTimeSlider
301 {
302     input_thread_t * p_input;
303     p_input = pl_CurrentInput(getIntf());
304
305     [self.timeSlider setHidden:NO];
306
307     if (!p_input) {
308         // Nothing playing
309         [self.timeSlider setKnobHidden:YES];
310         [self.timeSlider setFloatValue: 0.0];
311         [self.timeField setStringValue: @"00:00"];
312         [self.timeSlider setIndefinite:NO];
313         [self.timeSlider setEnabled:NO];
314         return;
315     }
316
317     [self.timeSlider setKnobHidden:NO];
318
319     vlc_value_t pos;
320     var_Get(p_input, "position", &pos);
321     [self.timeSlider setFloatValue:(10000. * pos.f_float)];
322
323     mtime_t dur = input_item_GetDuration(input_GetItem(p_input));
324     if (dur == -1) {
325         // No duration, disable slider
326         [self.timeSlider setEnabled:NO];
327     } else {
328         input_state_e inputState = input_GetState(p_input);
329         bool buffering = (inputState == INIT_S || inputState == OPENING_S);
330         [self.timeSlider setIndefinite:buffering];
331     }
332
333     NSString *time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString:p_input
334                                                                       negative:[self.timeField timeRemaining]];
335     [self.timeField setStringValue:time];
336     [self.timeField setNeedsDisplay:YES];
337
338     vlc_object_release(p_input);
339 }
340
341 - (void)updateControls
342 {
343     bool b_plmul = false;
344     bool b_seekable = false;
345     bool b_chapters = false;
346     bool b_buffering = false;
347
348     playlist_t * p_playlist = pl_Get(getIntf());
349
350     PL_LOCK;
351     b_plmul = playlist_CurrentSize(p_playlist) > 1;
352     PL_UNLOCK;
353
354     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
355
356     if (p_input) {
357         input_state_e inputState = input_GetState(p_input);
358         if (inputState == INIT_S || inputState == OPENING_S)
359             b_buffering = YES;
360
361         /* seekable streams */
362         b_seekable = var_GetBool(p_input, "can-seek");
363
364         /* chapters & titles */
365         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
366
367         vlc_object_release(p_input);
368     }
369
370     [self.timeSlider setEnabled: b_seekable];
371
372     [self.forwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
373     [self.backwardButton setEnabled: (b_seekable || b_plmul || b_chapters)];
374 }
375
376 - (void)setPause
377 {
378     [self.playButton setImage: _pauseImage];
379     [self.playButton setAlternateImage: _pressedPauseImage];
380     [self.playButton setToolTip: _NS("Pause")];
381 }
382
383 - (void)setPlay
384 {
385     [self.playButton setImage: _playImage];
386     [self.playButton setAlternateImage: _pressedPlayImage];
387     [self.playButton setToolTip: _NS("Play")];
388 }
389
390 - (void)setFullscreenState:(BOOL)b_fullscreen
391 {
392     if (!self.nativeFullscreenMode)
393         [self.fullscreenButton setState:b_fullscreen];
394 }
395
396 @end