]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: implemented graphics for forward/backward in the 6 button state
[vlc] / modules / gui / macosx / MainWindow.m
1 /*****************************************************************************
2  * MainWindow.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *          Jon Lech Johansen <jon-vl@nanocrew.net>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *          Derk-Jan Hartman <hartman at videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #import "CompatibilityFixes.h"
28 #import "MainWindow.h"
29 #import "intf.h"
30 #import "CoreInteraction.h"
31 #import "AudioEffects.h"
32 #import "MainMenu.h"
33 #import "open.h"
34 #import "controls.h" // TODO: remove me
35 #import "playlist.h"
36 #import "SideBarItem.h"
37 #import <math.h>
38 #import <vlc_playlist.h>
39 #import <vlc_aout_intf.h>
40 #import <vlc_url.h>
41 #import <vlc_strings.h>
42 #import <vlc_services_discovery.h>
43 #import <vlc_aout_intf.h>
44
45 @interface VLCMainWindow ()
46 - (void)addJumpButtons:(BOOL)b_fast;
47 - (void)removeJumpButtons:(BOOL)b_fast;
48 - (void)addPlaymodeButtons:(BOOL)b_fast;
49 - (void)removePlaymodeButtons:(BOOL)b_fast;
50
51 - (void)resetPreviousButton;
52 - (void)resetBackwardSkip;
53 - (void)resetNextButton;
54 - (void)resetForwardSkip;
55 - (void)resizePlaylistAfterCollapse;
56 - (void)makeSplitViewVisible;
57 - (void)makeSplitViewHidden;
58
59 - (NSRect)customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen;
60 @end
61
62 @implementation VLCMainWindow
63 static const float f_min_video_height = 70.0;
64
65 static VLCMainWindow *_o_sharedInstance = nil;
66
67 + (VLCMainWindow *)sharedInstance
68 {
69     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
70 }
71
72 #pragma mark -
73 #pragma mark Initialization
74
75 - (id)init
76 {
77     if( _o_sharedInstance )
78     {
79         [self dealloc];
80         return _o_sharedInstance;
81     }
82     else
83     {
84         _o_sharedInstance = [super init];
85     }
86
87     return _o_sharedInstance;
88 }
89
90 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
91                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
92 {
93     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
94
95     if (b_dark_interface)
96     {
97 #ifdef MAC_OS_X_VERSION_10_7
98         if (!OSX_SNOW_LEOPARD)
99             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
100         else
101             styleMask = NSBorderlessWindowMask;
102 #else
103         styleMask = NSBorderlessWindowMask;
104 #endif
105     }
106
107     self = [super initWithContentRect:contentRect styleMask:styleMask
108                               backing:backingType defer:flag];
109     _o_sharedInstance = self;
110
111     [[VLCMain sharedInstance] updateTogglePlaylistState];
112
113     /* we want to be moveable regardless of our style */
114     [self setMovableByWindowBackground: YES];
115
116     /* we don't want this window to be restored on relaunch */
117     if (!OSX_SNOW_LEOPARD)
118         [self setRestorable:NO];
119
120     return self;
121 }
122
123 - (BOOL)isEvent:(NSEvent *)o_event forKey:(const char *)keyString
124 {
125     char *key;
126     NSString *o_key;
127
128     key = config_GetPsz( VLCIntf, keyString );
129     o_key = [NSString stringWithFormat:@"%s", key];
130     FREENULL( key );
131
132     unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa:o_key];
133
134     NSString * characters = [o_event charactersIgnoringModifiers];
135     if ([characters length] > 0) {
136         return [[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: o_key]] &&
137                 (i_keyModifiers & NSShiftKeyMask)     == ([o_event modifierFlags] & NSShiftKeyMask) &&
138                 (i_keyModifiers & NSControlKeyMask)   == ([o_event modifierFlags] & NSControlKeyMask) &&
139                 (i_keyModifiers & NSAlternateKeyMask) == ([o_event modifierFlags] & NSAlternateKeyMask) &&
140                 (i_keyModifiers & NSCommandKeyMask)   == ([o_event modifierFlags] & NSCommandKeyMask);
141     }
142     return NO;
143 }
144
145 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
146 {
147     BOOL b_force = NO;
148     // these are key events which should be handled by vlc core, but are attached to a main menu item
149     if( ![self isEvent: o_event forKey: "key-vol-up"] &&
150         ![self isEvent: o_event forKey: "key-vol-down"] &&
151         ![self isEvent: o_event forKey: "key-vol-mute"] )
152     {
153         /* We indeed want to prioritize some Cocoa key equivalent against libvlc,
154          so we perform the menu equivalent now. */
155         if([[NSApp mainMenu] performKeyEquivalent:o_event])
156             return TRUE;
157     }
158     else
159         b_force = YES;
160
161     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event force:b_force] ||
162            [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
163 }
164
165 - (void)dealloc
166 {
167     if (b_dark_interface)
168         [o_color_backdrop release];
169
170     [[NSNotificationCenter defaultCenter] removeObserver: self];
171     [o_sidebaritems release];
172
173     if( o_extra_video_window )
174     {
175         [o_extra_video_window release];
176         o_extra_video_window = nil;
177     }
178
179     [super dealloc];
180 }
181
182 - (void)awakeFromNib
183 {
184     BOOL b_splitviewShouldBeHidden = NO;
185
186     /* setup the styled interface */
187     b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
188     b_nativeFullscreenMode = NO;
189 #ifdef MAC_OS_X_VERSION_10_7
190     if( !OSX_SNOW_LEOPARD && b_video_deco )
191         b_nativeFullscreenMode = var_InheritBool( VLCIntf, "macosx-nativefullscreenmode" );
192 #endif
193     t_hide_mouse_timer = nil;
194     [o_detached_video_window setDelegate: self];
195     [self useOptimizedDrawing: YES];
196
197     [o_play_btn setToolTip: _NS("Play/Pause")];
198     [[o_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
199     [o_detached_play_btn setToolTip: [o_play_btn toolTip]];
200     [[o_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
201     [[o_detached_play_btn cell] accessibilitySetOverrideValue:[o_play_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
202     [[o_detached_play_btn cell] accessibilitySetOverrideValue:_NS("Click to play or pause the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
203     [o_bwd_btn setToolTip: _NS("Backward")];
204     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
205     [o_detached_bwd_btn setToolTip: [o_bwd_btn toolTip]];
206     [[o_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
207     [[o_detached_bwd_btn cell] accessibilitySetOverrideValue:[o_bwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
208     [[o_detached_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
209     [o_fwd_btn setToolTip: _NS("Forward")];
210     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
211     [o_detached_fwd_btn setToolTip: [o_fwd_btn toolTip]];
212     [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
213     [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:[o_fwd_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
214     [[o_detached_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
215     [o_stop_btn setToolTip: _NS("Stop")];
216     [[o_stop_btn cell] accessibilitySetOverrideValue:_NS("Click to stop playback.") forAttribute:NSAccessibilityDescriptionAttribute];
217     [[o_stop_btn cell] accessibilitySetOverrideValue:[o_stop_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
218     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
219     [[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];
220     [[o_playlist_btn cell] accessibilitySetOverrideValue:[o_playlist_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
221     [o_repeat_btn setToolTip: _NS("Repeat")];
222     [[o_repeat_btn cell] accessibilitySetOverrideValue:_NS("Click to change repeat mode. There are 3 states: repeat one, repeat all and off.") forAttribute:NSAccessibilityDescriptionAttribute];
223     [[o_repeat_btn cell] accessibilitySetOverrideValue:[o_repeat_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
224     [o_shuffle_btn setToolTip: _NS("Shuffle")];
225     [[o_shuffle_btn cell] accessibilitySetOverrideValue:[o_shuffle_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
226     [[o_shuffle_btn cell] accessibilitySetOverrideValue:_NS("Click to enable or disable random playback.") forAttribute:NSAccessibilityDescriptionAttribute];
227     [o_effects_btn setToolTip: _NS("Effects")];
228     [[o_effects_btn cell] accessibilitySetOverrideValue:_NS("Click to show an Audio Effects panel featuring an equalizer and further filters.") forAttribute:NSAccessibilityDescriptionAttribute];
229     [[o_effects_btn cell] accessibilitySetOverrideValue:[o_effects_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
230     [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
231     [[o_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
232     [o_detached_fullscreen_btn setToolTip: [o_fullscreen_btn toolTip]];
233     [[o_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
234     [[o_detached_fullscreen_btn cell] accessibilitySetOverrideValue:[o_fullscreen_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
235     [[o_detached_fullscreen_btn cell] accessibilitySetOverrideValue:_NS("Click to enable fullscreen video playback.") forAttribute:NSAccessibilityDescriptionAttribute];
236     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
237     [[o_search_fld cell] accessibilitySetOverrideValue:_NS("Enter a term to search the playlist. Results will be selected in the table.") forAttribute:NSAccessibilityDescriptionAttribute];
238     [o_volume_sld setToolTip: _NS("Volume")];
239     [[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];
240     [[o_volume_sld cell] accessibilitySetOverrideValue:[o_volume_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
241     [o_volume_down_btn setToolTip: _NS("Mute")];
242     [[o_volume_down_btn cell] accessibilitySetOverrideValue:_NS("Click to mute or unmute the audio.") forAttribute:NSAccessibilityDescriptionAttribute];
243     [[o_volume_down_btn cell] accessibilitySetOverrideValue:[o_volume_down_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
244     [o_volume_up_btn setToolTip: _NS("Full Volume")];
245     [[o_volume_up_btn cell] accessibilitySetOverrideValue:_NS("Click to play the audio at maximum volume.") forAttribute:NSAccessibilityDescriptionAttribute];
246     [[o_volume_up_btn cell] accessibilitySetOverrideValue:[o_volume_up_btn toolTip] forAttribute:NSAccessibilityTitleAttribute];
247     [o_time_sld setToolTip: _NS("Position")];
248     [[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];
249     [[o_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
250     [o_detached_time_sld setToolTip: [o_time_sld toolTip]];
251     [[o_detached_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];
252     [[o_detached_time_sld cell] accessibilitySetOverrideValue:[o_time_sld toolTip] forAttribute:NSAccessibilityTitleAttribute];
253     [o_dropzone_btn setTitle: _NS("Open media...")];
254     [[o_dropzone_btn cell] accessibilitySetOverrideValue:_NS("Click to open an advanced dialog to select the media to play. You can also drop files here to play.") forAttribute:NSAccessibilityDescriptionAttribute];
255     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
256
257     [o_podcast_add_btn setTitle: _NS("Subscribe")];
258     [o_podcast_remove_btn setTitle: _NS("Unsubscribe")];
259     [o_podcast_subscribe_title_lbl setStringValue: _NS("Subscribe to a podcast")];
260     [o_podcast_subscribe_subtitle_lbl setStringValue: _NS("Enter URL of the podcast to subscribe to:")];
261     [o_podcast_subscribe_cancel_btn setTitle: _NS("Cancel")];
262     [o_podcast_subscribe_ok_btn setTitle: _NS("Subscribe")];
263     [o_podcast_unsubscribe_title_lbl setStringValue: _NS("Unsubscribe from a podcast")];
264     [o_podcast_unsubscribe_subtitle_lbl setStringValue: _NS("Select the podcast you would like to unsubscribe from:")];
265     [o_podcast_unsubscribe_ok_btn setTitle: _NS("Unsubscribe")];
266     [o_podcast_unsubscribe_cancel_btn setTitle: _NS("Cancel")];
267
268     if (!b_dark_interface) {
269         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
270         [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
271         [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns"]];
272         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-pressed"]];
273         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns"]];
274         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-pressed"]];
275         o_play_img = [[NSImage imageNamed:@"play"] retain];
276         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
277         o_pause_img = [[NSImage imageNamed:@"pause"] retain];
278         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
279         [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns"]];
280         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-pressed"]];
281         [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns"]];
282         [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-pressed"]];
283         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
284         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
285         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist-btn"]];
286         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-btn-pressed"]];
287         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
288         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
289         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
290         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
291         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
292         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
293         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
294         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
295         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
296         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
297         [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"]];
298         [o_detached_time_sld_background setImagesLeft: [NSImage imageNamed:@"progression-track-wrapper-left"] middle: [NSImage imageNamed:@"progression-track-wrapper-middle"] right: [NSImage imageNamed:@"progression-track-wrapper-right"]];
299         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
300         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
301         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
302         if (b_nativeFullscreenMode)
303         {
304             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
305             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
306         }
307         else
308         {
309             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
310             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
311         }
312         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
313         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
314         [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button"]];
315         [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed"]];
316         [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
317         [o_detached_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
318     }
319     else
320     {
321         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
322         [o_detached_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
323         [o_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns-dark"]];
324         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-dark-pressed"]];
325         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"backward-3btns-dark"]];
326         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"backward-3btns-dark-pressed"]];
327         o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
328         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
329         o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
330         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
331         [o_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
332         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
333         [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward-3btns-dark"]];
334         [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-3btns-dark-pressed"]];
335         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
336         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
337         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
338         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
339         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
340         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
341         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
342         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
343         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
344         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
345         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
346         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
347         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
348         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
349         [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"]];
350         [o_detached_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"]];
351         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
352         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
353         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
354         if (b_nativeFullscreenMode)
355         {
356             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
357             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
358         }
359         else
360         {
361             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
362             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
363         }
364         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
365         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
366         [o_detached_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-one-button_dark"]];
367         [o_detached_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-one-button-pressed_dark"]];
368         [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"]];
369         [o_detached_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"]];
370     }
371     [o_repeat_btn setImage: o_repeat_img];
372     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
373     [o_shuffle_btn setImage: o_shuffle_img];
374     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
375     [o_play_btn setImage: o_play_img];
376     [o_play_btn setAlternateImage: o_play_pressed_img];
377     [o_detached_play_btn setImage: o_play_img];
378     [o_detached_play_btn setAlternateImage: o_play_pressed_img];
379     BOOL b_mute = ![[VLCCoreInteraction sharedInstance] mute];
380     [o_volume_sld setEnabled: b_mute];
381     [o_volume_up_btn setEnabled: b_mute];
382
383     b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
384     if (b_show_jump_buttons)
385         [self addJumpButtons:YES];
386
387     b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
388     if (!b_show_playmode_buttons)
389         [self removePlaymodeButtons:YES];
390
391     /* interface builder action */
392     float f_threshold_height = f_min_video_height + [o_bottombar_view frame].size.height;
393     if( b_dark_interface )
394         f_threshold_height += [o_titlebar_view frame].size.height;
395     if( [[self contentView] frame].size.height < f_threshold_height )
396         b_splitviewShouldBeHidden = YES;
397
398     [self setDelegate: self];
399     [self setExcludedFromWindowsMenu: YES];
400     [self setAcceptsMouseMovedEvents: YES];
401     // Set that here as IB seems to be buggy
402     if (b_dark_interface && b_video_deco)
403     {
404         [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
405         [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height + [o_detached_titlebar_view frame].size.height )];
406     }
407     else if( b_video_deco )
408     {
409         [self setContentMinSize:NSMakeSize(604., 288.)];
410         [o_detached_video_window setContentMinSize: NSMakeSize( 363., f_min_video_height + [o_detached_bottombar_view frame].size.height )];
411     }
412     else
413     {   // !b_video_deco:
414         if (b_dark_interface)
415             [self setContentMinSize:NSMakeSize(604., 288. + [o_titlebar_view frame].size.height)];
416         else
417             [self setContentMinSize:NSMakeSize(604., 288.)];
418
419         [o_detached_bottombar_view setHidden:YES];
420         [o_detached_video_window setContentMinSize: NSMakeSize( f_min_video_height, f_min_video_height )];
421     }
422
423     [self setTitle: _NS("VLC media player")];
424     [o_time_fld setAlignment: NSCenterTextAlignment];
425     [o_time_fld setNeedsDisplay:YES];
426     b_dropzone_active = YES;
427     o_temp_view = [[NSView alloc] init];
428     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
429     [o_dropzone_view setFrame: [o_playlist_table frame]];
430     [o_left_split_view setFrame: [o_sidebar_view frame]];
431     if (b_nativeFullscreenMode)
432     {
433         NSRect frame;
434         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
435         float f_width = [o_fullscreen_btn frame].size.width;
436
437         #define moveItem( item ) \
438         frame = [item frame]; \
439         frame.origin.x = f_width + frame.origin.x; \
440         [item setFrame: frame]
441
442         moveItem( o_effects_btn );
443         moveItem( o_volume_up_btn );
444         moveItem( o_volume_sld );
445         moveItem( o_volume_track_view );
446         moveItem( o_volume_down_btn );
447         moveItem( o_time_fld );
448         #undef moveItem
449
450         #define enlargeItem( item ) \
451         frame = [item frame]; \
452         frame.size.width = f_width + frame.size.width; \
453         [item setFrame: frame]
454
455         enlargeItem( o_time_sld );
456         enlargeItem( o_progress_bar );
457         enlargeItem( o_time_sld_background );
458         enlargeItem( o_time_sld_fancygradient_view );
459         #undef enlargeItem
460
461         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
462     }
463     else
464     {
465         [o_titlebar_view setFullscreenButtonHidden: YES];
466         if (b_video_deco)
467             [o_detached_titlebar_view setFullscreenButtonHidden: YES];
468     }
469
470     if (!OSX_SNOW_LEOPARD)
471     {
472         /* the default small size of the search field is slightly different on Lion, let's work-around that */
473         NSRect frame;
474         frame = [o_search_fld frame];
475         frame.origin.y = frame.origin.y + 2.0;
476         frame.size.height = frame.size.height - 1.0;
477         [o_search_fld setFrame: frame];
478     }
479
480     /* create the sidebar */
481     o_sidebaritems = [[NSMutableArray alloc] init];
482     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
483     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
484     [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
485     SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
486     [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
487     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
488     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
489     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
490     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
491
492     /* SD subnodes, inspired by the Qt4 intf */
493     char **ppsz_longnames;
494     int *p_categories;
495     char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
496     if (!ppsz_names)
497         msg_Err( VLCIntf, "no sd item found" ); //TODO
498     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
499     int *p_category = p_categories;
500     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
501     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
502     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
503     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
504     NSString *o_identifier;
505     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
506     {
507         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
508         switch (*p_category) {
509             case SD_CAT_INTERNET:
510                 {
511                     [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
512                     if (!strncmp( *ppsz_name, "podcast", 7 ))
513                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
514                     else
515                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
516                     [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
517                     [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
518                 }
519                 break;
520             case SD_CAT_DEVICES:
521                 {
522                     [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
523                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
524                     [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
525                     [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
526                 }
527                 break;
528             case SD_CAT_LAN:
529                 {
530                     [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
531                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
532                     [[lanItems lastObject] setSdtype: SD_CAT_LAN];
533                     [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
534                 }
535                 break;
536             case SD_CAT_MYCOMPUTER:
537                 {
538                     [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
539                     if (!strncmp( *ppsz_name, "video_dir", 9 ))
540                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
541                     else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
542                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
543                     else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
544                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
545                     else
546                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
547                     [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
548                     [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
549                 }
550                 break;
551             default:
552                 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
553                 break;
554         }
555
556         free( *ppsz_name );
557         free( *ppsz_longname );
558     }
559     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
560     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
561     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
562     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
563     [mycompItems release];
564     [devicesItems release];
565     [lanItems release];
566     [internetItems release];
567     free( ppsz_names );
568     free( ppsz_longnames );
569     free( p_categories );
570
571     [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
572     [o_sidebaritems addObject: libraryItem];
573     if ([mycompItem hasChildren])
574         [o_sidebaritems addObject: mycompItem];
575     if ([devicesItem hasChildren])
576         [o_sidebaritems addObject: devicesItem];
577     if ([lanItem hasChildren])
578         [o_sidebaritems addObject: lanItem];
579     if ([internetItem hasChildren])
580         [o_sidebaritems addObject: internetItem];
581
582     [o_sidebar_view reloadData];
583     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
584     [o_sidebar_view setDropItem:playlistItem dropChildIndex:NSOutlineViewDropOnItemIndex];
585     [o_sidebar_view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
586
587     [o_sidebar_view setAutosaveName:@"mainwindow-sidebar"];
588     [(PXSourceList *)o_sidebar_view setDataSource:self];
589     [o_sidebar_view setDelegate:self];
590     [o_sidebar_view setAutosaveExpandedItems:YES];
591
592     [o_sidebar_view expandItem: libraryItem expandChildren: YES];
593
594     /* make sure we display the desired default appearance when VLC launches for the first time */
595     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
596     if (![defaults objectForKey:@"VLCFirstRun"])
597     {
598         [defaults setObject:[NSDate date] forKey:@"VLCFirstRun"];
599
600         NSUInteger i_sidebaritem_count = [o_sidebaritems count];
601         for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
602             [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
603     }
604
605     if( b_dark_interface )
606     {
607         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
608         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
609
610         [self setBackgroundColor: [NSColor clearColor]];
611         [self setOpaque: NO];
612         [self display];
613         [self setHasShadow:NO];
614         [self setHasShadow:YES];
615
616         NSRect winrect = [self frame];
617         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
618
619         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
620                                               winrect.size.width, f_titleBarHeight )];
621         [[self contentView] addSubview: o_titlebar_view positioned: NSWindowAbove relativeTo: o_split_view];
622
623         if (winrect.size.height > 100)
624         {
625             [self setFrame: winrect display:YES animate:YES];
626             previousSavedFrame = winrect;
627         }
628
629         winrect = [o_split_view frame];
630         winrect.size.height = winrect.size.height - f_titleBarHeight;
631         [o_split_view setFrame: winrect];
632         [o_video_view setFrame: winrect];
633
634         /* detached video window */
635         winrect = [o_detached_video_window frame];
636         if (b_video_deco)
637         {
638             [o_detached_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight, winrect.size.width, f_titleBarHeight )];
639             [[o_detached_video_window contentView] addSubview: o_detached_titlebar_view positioned: NSWindowAbove relativeTo: nil];
640         }
641
642         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
643         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
644         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
645     }
646     else
647     {
648         NSRect frame;
649         frame = [o_time_sld_fancygradient_view frame];
650         frame.size.height = frame.size.height - 1;
651         frame.origin.y = frame.origin.y + 1;
652         [o_time_sld_fancygradient_view setFrame: frame];
653
654         frame = [o_detached_time_sld_fancygradient_view frame];
655         frame.size.height = frame.size.height - 1;
656         frame.origin.y = frame.origin.y + 1;
657         [o_detached_time_sld_fancygradient_view setFrame: frame];
658
659         [o_video_view setFrame: [o_split_view frame]];
660         [o_playlist_table setBorderType: NSNoBorder];
661         [o_sidebar_scrollview setBorderType: NSNoBorder];
662     }
663
664     NSRect frame;
665     frame = [o_time_sld_fancygradient_view frame];
666     frame.size.width = 0;
667     [o_time_sld_fancygradient_view setFrame: frame];
668
669     frame = [o_detached_time_sld_fancygradient_view frame];
670     frame.size.width = 0;
671     [o_detached_time_sld_fancygradient_view setFrame: frame];
672
673     if (!OSX_SNOW_LEOPARD)
674     {
675         [o_resize_view setImage: NULL];
676         [o_detached_resize_view setImage: NULL];
677     }
678
679     if ([self styleMask] & NSResizableWindowMask)
680     {
681         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
682         [o_detached_resize_view removeFromSuperviewWithoutNeedingDisplay];
683     }
684
685     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
686     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
687     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
688     [[VLCMain sharedInstance] playbackModeUpdated];
689
690     [o_split_view setAutosaveName:@"10thanniversary-splitview"];
691     if (b_splitviewShouldBeHidden)
692     {
693         [self hideSplitView];
694         i_lastSplitViewHeight = 300;
695     }
696
697     /* sanity check for the window size */
698     frame = [self frame];
699     NSSize screenSize = [[self screen] frame].size;
700     if (screenSize.width <= frame.size.width || screenSize.height <= frame.size.height) {
701         nativeVideoSize = screenSize;
702         [self resizeWindow];
703     }
704 }
705
706 #pragma mark -
707 #pragma mark interface customization
708 - (void)toggleJumpButtons
709 {
710     b_show_jump_buttons = config_GetInt( VLCIntf, "macosx-show-playback-buttons" );
711
712     if (b_show_jump_buttons)
713         [self addJumpButtons:NO];
714     else
715         [self removeJumpButtons:NO];
716 }
717
718 - (void)addJumpButtons:(BOOL)b_fast
719 {
720     NSRect preliminaryFrame = [o_bwd_btn frame];
721     BOOL b_enabled = [o_bwd_btn isEnabled];
722     preliminaryFrame.size.width = 29.;
723     o_prev_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
724     [o_prev_btn setButtonType: NSMomentaryChangeButton];
725     [o_prev_btn setBezelStyle:NSRegularSquareBezelStyle];
726     [o_prev_btn setBordered:NO];
727     [o_prev_btn setTarget:self];
728     [o_prev_btn setAction:@selector(prev:)];
729     [o_prev_btn setToolTip: _NS("Previous")];
730     [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Previous") forAttribute:NSAccessibilityTitleAttribute];
731     [[o_prev_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
732     [o_prev_btn setEnabled: b_enabled];
733
734     o_next_btn = [[NSButton alloc] initWithFrame:preliminaryFrame];
735     [o_next_btn setButtonType: NSMomentaryChangeButton];
736     [o_next_btn setBezelStyle:NSRegularSquareBezelStyle];
737     [o_next_btn setBordered:NO];
738     [o_next_btn setTarget:self];
739     [o_next_btn setAction:@selector(next:)];
740     [o_next_btn setToolTip: _NS("Next")];
741     [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Next") forAttribute:NSAccessibilityTitleAttribute];
742     [[o_next_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item.") forAttribute:NSAccessibilityDescriptionAttribute];
743     [o_next_btn setEnabled: b_enabled];
744
745     if (b_dark_interface) {
746         [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns-dark"]];
747         [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-dark-pressed"]];
748         [o_next_btn setImage: [NSImage imageNamed:@"next-6btns-dark"]];
749         [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-dark-pressed"]];
750     } else {
751         [o_prev_btn setImage: [NSImage imageNamed:@"previous-6btns"]];
752         [o_prev_btn setAlternateImage: [NSImage imageNamed:@"previous-6btns-pressed"]];
753         [o_next_btn setImage: [NSImage imageNamed:@"next-6btns"]];
754         [o_next_btn setAlternateImage: [NSImage imageNamed:@"next-6btns-pressed"]];
755     }
756
757     /* change the accessibility help for the backward/forward buttons accordingly */
758     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
759     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click and hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
760
761     NSRect frame;
762     frame = [o_bwd_btn frame];
763     frame.size.width++;
764     [o_bwd_btn setFrame:frame];
765     frame = [o_fwd_btn frame];
766     frame.size.width++;
767     [o_fwd_btn setFrame:frame];
768
769     float f_space = 29.;
770     #define moveItem( item ) \
771     frame = [item frame]; \
772     frame.origin.x = frame.origin.x + f_space; \
773     if( b_fast ) \
774         [item setFrame: frame]; \
775     else \
776         [[item animator] setFrame: frame]
777
778     moveItem( o_bwd_btn );
779     moveItem( o_play_btn );
780     f_space = 28.;
781     moveItem( o_fwd_btn );
782     f_space = 57.;
783     moveItem( o_stop_btn );
784     moveItem( o_playlist_btn );
785     moveItem( o_repeat_btn );
786     moveItem( o_shuffle_btn );
787     #undef moveItem
788
789     #define resizeItem( item ) \
790     frame = [item frame]; \
791     frame.size.width = frame.size.width - f_space; \
792     frame.origin.x = frame.origin.x + f_space; \
793     if( b_fast ) \
794         [item setFrame: frame]; \
795     else \
796         [[item animator] setFrame: frame]
797
798     resizeItem( o_time_sld );
799     resizeItem( o_progress_bar );
800     resizeItem( o_time_sld_background );
801     resizeItem( o_time_sld_fancygradient_view );
802     #undef resizeItem
803
804     if (b_dark_interface) {
805         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns-dark"]];
806         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-dark-pressed"]];
807         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns-dark"]];
808         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-dark-pressed"]];
809     } else {
810         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-6btns"]];
811         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-6btns-pressed"]];
812         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-6btns"]];
813         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-6btns-pressed"]];
814     }
815
816     preliminaryFrame.origin.x = [o_next_btn frame].origin.x + 82. + [o_fwd_btn frame].size.width;
817     [o_next_btn setFrame: preliminaryFrame];
818
819     // wait until the animation is done, if displayed
820     if (b_fast) {
821         [[self contentView] addSubview:o_prev_btn];
822         [[self contentView] addSubview:o_next_btn];
823     } else {
824         [[self contentView] performSelector:@selector(addSubview:) withObject:o_prev_btn afterDelay:.2];
825         [[self contentView] performSelector:@selector(addSubview:) withObject:o_next_btn afterDelay:.2];
826     }
827
828     [o_fwd_btn setAction:@selector(forward:)];
829     [o_bwd_btn setAction:@selector(backward:)];
830 }
831
832 - (void)removeJumpButtons:(BOOL)b_fast
833 {
834     if (!o_prev_btn || !o_next_btn )
835         return;
836
837     if( b_fast ) {
838         [o_prev_btn setHidden: YES];
839         [o_next_btn setHidden: YES];
840     } else {
841         [[o_prev_btn animator] setHidden: YES];
842         [[o_next_btn animator] setHidden: YES];
843     }
844     [o_prev_btn removeFromSuperviewWithoutNeedingDisplay];
845     [o_next_btn removeFromSuperviewWithoutNeedingDisplay];
846     [o_prev_btn release];
847     [o_next_btn release];
848
849     /* change the accessibility help for the backward/forward buttons accordingly */
850     [[o_bwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the previous playlist item. Hold to skip backward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
851     [[o_fwd_btn cell] accessibilitySetOverrideValue:_NS("Click to go to the next playlist item. Hold to skip forward through the current media.") forAttribute:NSAccessibilityDescriptionAttribute];
852
853     NSRect frame;
854     frame = [o_bwd_btn frame];
855     frame.size.width--;
856     [o_bwd_btn setFrame:frame];
857     frame = [o_fwd_btn frame];
858     frame.size.width--;
859     [o_fwd_btn setFrame:frame];
860
861     float f_space = 29.;
862     #define moveItem( item ) \
863     frame = [item frame]; \
864     frame.origin.x = frame.origin.x - f_space; \
865     if( b_fast ) \
866         [item setFrame: frame]; \
867     else \
868         [[item animator] setFrame: frame]
869
870     moveItem( o_bwd_btn );
871     moveItem( o_play_btn );
872     f_space = 28.;
873     moveItem( o_fwd_btn );
874     f_space = 57.;
875     moveItem( o_stop_btn );
876     moveItem( o_playlist_btn );
877     moveItem( o_repeat_btn );
878     moveItem( o_shuffle_btn );
879     #undef moveItem
880
881     #define resizeItem( item ) \
882     frame = [item frame]; \
883     frame.size.width = frame.size.width + f_space; \
884     frame.origin.x = frame.origin.x - f_space; \
885     if( b_fast ) \
886         [item setFrame: frame]; \
887     else \
888         [[item animator] setFrame: frame]
889
890     resizeItem( o_time_sld );
891     resizeItem( o_progress_bar );
892     resizeItem( o_time_sld_background );
893     resizeItem( o_time_sld_fancygradient_view );
894     #undef resizeItem
895
896     if (b_dark_interface) {
897         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns-dark"]];
898         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-dark-pressed"]];
899         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns-dark"]];
900         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-dark-pressed"]];
901     } else {
902         [[o_fwd_btn animator] setImage:[NSImage imageNamed:@"forward-3btns"]];
903         [[o_fwd_btn animator] setAlternateImage:[NSImage imageNamed:@"forward-3btns-pressed"]];
904         [[o_bwd_btn animator] setImage:[NSImage imageNamed:@"backward-3btns"]];
905         [[o_bwd_btn animator] setAlternateImage:[NSImage imageNamed:@"backward-3btns-pressed"]];
906     }
907
908     [o_bottombar_view setNeedsDisplay:YES];
909
910     [o_fwd_btn setAction:@selector(fwd:)];
911     [o_bwd_btn setAction:@selector(bwd:)];
912 }
913
914 - (void)togglePlaymodeButtons
915 {
916     b_show_playmode_buttons = config_GetInt( VLCIntf, "macosx-show-playmode-buttons" );
917
918     if (b_show_playmode_buttons)
919         [self addPlaymodeButtons:NO];
920     else
921         [self removePlaymodeButtons:NO];
922 }
923
924 - (void)addPlaymodeButtons:(BOOL)b_fast
925 {
926     NSRect frame;
927     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
928
929     if (b_dark_interface) {
930         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist_dark"]];
931         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-pressed_dark"]];
932     } else {
933         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-btn"]];
934         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-btn-pressed"]];
935     }
936     frame = [o_playlist_btn frame];
937     frame.size.width--;
938     [o_playlist_btn setFrame:frame];
939
940     if (b_fast) {
941         [o_repeat_btn setHidden: NO];
942         [o_shuffle_btn setHidden: NO];
943     } else {
944         [[o_repeat_btn animator] setHidden: NO];
945         [[o_shuffle_btn animator] setHidden: NO];
946     }
947
948     #define resizeItem( item ) \
949     frame = [item frame]; \
950     frame.size.width = frame.size.width - f_space; \
951     frame.origin.x = frame.origin.x + f_space; \
952     if( b_fast ) \
953         [item setFrame: frame]; \
954     else \
955         [[item animator] setFrame: frame]
956
957     resizeItem( o_time_sld );
958     resizeItem( o_progress_bar );
959     resizeItem( o_time_sld_background );
960     resizeItem( o_time_sld_fancygradient_view );
961     #undef resizeItem
962 }
963
964 - (void)removePlaymodeButtons:(BOOL)b_fast
965 {
966     NSRect frame;
967     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
968     [o_repeat_btn setHidden: YES];
969     [o_shuffle_btn setHidden: YES];
970
971     if (b_dark_interface) {
972         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn-dark"]];
973         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-dark-pressed"]];
974     } else {
975         [[o_playlist_btn animator] setImage:[NSImage imageNamed:@"playlist-1btn"]];
976         [[o_playlist_btn animator] setAlternateImage:[NSImage imageNamed:@"playlist-1btn-pressed"]];
977     }
978     frame = [o_playlist_btn frame];
979     frame.size.width++;
980     [o_playlist_btn setFrame:frame];
981
982     #define resizeItem( item ) \
983     frame = [item frame]; \
984     frame.size.width = frame.size.width + f_space; \
985     frame.origin.x = frame.origin.x - f_space; \
986     if( b_fast ) \
987         [item setFrame: frame]; \
988     else \
989         [[item animator] setFrame: frame]
990
991     resizeItem( o_time_sld );
992     resizeItem( o_progress_bar );
993     resizeItem( o_time_sld_background );
994     resizeItem( o_time_sld_fancygradient_view );
995     #undef resizeItem
996 }
997
998 #pragma mark -
999 #pragma mark Button Actions
1000
1001 - (IBAction)play:(id)sender
1002 {
1003     [[VLCCoreInteraction sharedInstance] play];
1004 }
1005
1006 - (void)resetPreviousButton
1007 {
1008     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
1009         // seems like no further event occurred, so let's switch the playback item
1010         [[VLCCoreInteraction sharedInstance] previous];
1011         just_triggered_previous = NO;
1012     }
1013 }
1014
1015 - (void)resetBackwardSkip
1016 {
1017     // the user stopped skipping, so let's allow him to change the item
1018     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
1019         just_triggered_previous = NO;
1020 }
1021
1022 - (IBAction)prev:(id)sender
1023 {
1024     [[VLCCoreInteraction sharedInstance] previous];
1025 }
1026
1027 - (IBAction)bwd:(id)sender
1028 {
1029     if(!just_triggered_previous)
1030     {
1031         just_triggered_previous = YES;
1032         [self performSelector:@selector(resetPreviousButton)
1033                    withObject: NULL
1034                    afterDelay:0.40];
1035     }
1036     else
1037     {
1038         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1039         {
1040             // we just skipped 4 "continous" events, otherwise we are too fast
1041             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
1042             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
1043             [self performSelector:@selector(resetBackwardSkip)
1044                        withObject: NULL
1045                        afterDelay:0.40];
1046         }
1047     }
1048 }
1049
1050 - (IBAction)backward:(id)sender
1051 {
1052     [[VLCCoreInteraction sharedInstance] backwardShort];
1053 }
1054
1055 - (void)resetNextButton
1056 {
1057     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
1058         // seems like no further event occurred, so let's switch the playback item
1059         [[VLCCoreInteraction sharedInstance] next];
1060         just_triggered_next = NO;
1061     }
1062 }
1063
1064 - (void)resetForwardSkip
1065 {
1066     // the user stopped skipping, so let's allow him to change the item
1067     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
1068         just_triggered_next = NO;
1069 }
1070
1071 - (IBAction)next:(id)sender
1072 {
1073     [[VLCCoreInteraction sharedInstance] next];
1074 }
1075
1076 - (IBAction)forward:(id)sender
1077 {
1078     [[VLCCoreInteraction sharedInstance] forwardShort];
1079 }
1080
1081 - (IBAction)fwd:(id)sender
1082 {
1083    if(!just_triggered_next)
1084     {
1085         just_triggered_next = YES;
1086         [self performSelector:@selector(resetNextButton)
1087                    withObject: NULL
1088                    afterDelay:0.40];
1089     }
1090     else
1091     {
1092         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1093         {
1094             // we just skipped 4 "continous" events, otherwise we are too fast
1095             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
1096             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
1097             [self performSelector:@selector(resetForwardSkip)
1098                        withObject: NULL
1099                        afterDelay:0.40];
1100         }
1101     }
1102 }
1103
1104 - (IBAction)stop:(id)sender
1105 {
1106     [[VLCCoreInteraction sharedInstance] stop];
1107 }
1108
1109 - (void)resizePlaylistAfterCollapse
1110 {
1111     NSRect plrect;
1112     plrect = [o_playlist_table frame];
1113     plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
1114     [[o_playlist_table animator] setFrame: plrect];
1115
1116     NSRect rightSplitRect;
1117     rightSplitRect = [o_right_split_view frame];
1118     plrect = [o_dropzone_box frame];
1119     plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
1120     plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
1121     [[o_dropzone_box animator] setFrame: plrect];
1122 }
1123
1124 - (void)makeSplitViewVisible
1125 {
1126     if( b_dark_interface )
1127         [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1128     else
1129         [self setContentMinSize: NSMakeSize( 604., 288. )];
1130
1131     NSRect old_frame = [self frame];
1132     float newHeight = [self minSize].height;
1133     if( old_frame.size.height < newHeight )
1134     {
1135         NSRect new_frame = old_frame;
1136         new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1137         new_frame.size.height = newHeight;
1138
1139         [[self animator] setFrame: new_frame display: YES animate: YES];
1140     }
1141
1142     [o_video_view setHidden: YES];
1143     [o_split_view setHidden: NO];
1144     [self makeFirstResponder: nil];
1145
1146 }
1147
1148 - (void)makeSplitViewHidden
1149 {
1150     if( b_dark_interface )
1151         [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1152     else
1153         [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1154
1155     [o_split_view setHidden: YES];
1156     [o_video_view setHidden: NO];
1157
1158     if( [[o_video_view subviews] count] > 0 )
1159         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1160 }
1161
1162 - (IBAction)togglePlaylist:(id)sender
1163 {
1164     if (![self isVisible] && sender != nil)
1165     {
1166         [self makeKeyAndOrderFront: sender];
1167         return;
1168     }
1169
1170     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1171     BOOL b_restored = NO;
1172
1173     // TODO: implement toggle playlist in this situation (triggerd via menu item).
1174     // but for now we block this case, to avoid displaying only the half
1175     if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1176         return;
1177
1178     if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1179     {
1180         [self hideDropZone];
1181         return;
1182     }
1183
1184     if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1185                                   || (b_nonembedded && sender != nil)
1186                                   || (!b_activeVideo && sender != nil)
1187                                   || b_minimized_view ) )
1188     {
1189         [self hideSplitView];
1190     }
1191     else
1192     {
1193         if (b_splitview_removed)
1194         {
1195             if( !b_nonembedded || ( sender != nil && b_nonembedded))
1196                 [self showSplitView];
1197
1198             if (sender == nil)
1199                 b_minimized_view = YES;
1200             else
1201                 b_minimized_view = NO;
1202
1203             if (b_activeVideo)
1204                 b_restored = YES;
1205         }
1206
1207         if (!b_nonembedded)
1208         {
1209             if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1210                 [self makeSplitViewHidden];
1211             else
1212                 [self makeSplitViewVisible];
1213         }
1214         else
1215         {
1216             [o_split_view setHidden: NO];
1217             [o_playlist_table setHidden: NO];
1218             [o_video_view setHidden: !b_activeVideo];
1219             if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1220                 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1221         }
1222     }
1223 }
1224
1225 - (void)setRepeatOne
1226 {
1227     [o_repeat_btn setImage: o_repeat_one_img];
1228     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1229 }
1230
1231 - (void)setRepeatAll
1232 {
1233     [o_repeat_btn setImage: o_repeat_all_img];
1234     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1235 }
1236
1237 - (void)setRepeatOff
1238 {
1239     [o_repeat_btn setImage: o_repeat_img];
1240     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1241 }
1242
1243 - (IBAction)repeat:(id)sender
1244 {
1245     vlc_value_t looping,repeating;
1246     intf_thread_t * p_intf = VLCIntf;
1247     playlist_t * p_playlist = pl_Get( p_intf );
1248
1249     var_Get( p_playlist, "repeat", &repeating );
1250     var_Get( p_playlist, "loop", &looping );
1251
1252     if( !repeating.b_bool && !looping.b_bool )
1253     {
1254         /* was: no repeating at all, switching to Repeat One */
1255         [[VLCCoreInteraction sharedInstance] repeatOne];
1256         [self setRepeatOne];
1257     }
1258     else if( repeating.b_bool && !looping.b_bool )
1259     {
1260         /* was: Repeat One, switching to Repeat All */
1261         [[VLCCoreInteraction sharedInstance] repeatAll];
1262         [self setRepeatAll];
1263     }
1264     else
1265     {
1266         /* was: Repeat All or bug in VLC, switching to Repeat Off */
1267         [[VLCCoreInteraction sharedInstance] repeatOff];
1268         [self setRepeatOff];
1269     }
1270 }
1271
1272 - (void)setShuffle
1273 {
1274     bool b_value;
1275     playlist_t *p_playlist = pl_Get( VLCIntf );
1276     b_value = var_GetBool( p_playlist, "random" );
1277
1278     if(b_value) {
1279         [o_shuffle_btn setImage: o_shuffle_on_img];
1280         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1281     }
1282     else
1283     {
1284         [o_shuffle_btn setImage: o_shuffle_img];
1285         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1286     }
1287 }
1288
1289 - (IBAction)shuffle:(id)sender
1290 {
1291     [[VLCCoreInteraction sharedInstance] shuffle];
1292     [self setShuffle];
1293 }
1294
1295 - (IBAction)timeSliderAction:(id)sender
1296 {
1297     float f_updated;
1298     input_thread_t * p_input;
1299
1300     switch( [[NSApp currentEvent] type] )
1301     {
1302         case NSLeftMouseUp:
1303         case NSLeftMouseDown:
1304         case NSLeftMouseDragged:
1305             f_updated = [sender floatValue];
1306             break;
1307
1308         default:
1309             return;
1310     }
1311     p_input = pl_CurrentInput( VLCIntf );
1312     if( p_input != NULL )
1313     {
1314         vlc_value_t pos;
1315         NSString * o_time;
1316
1317         pos.f_float = f_updated / 10000.;
1318         var_Set( p_input, "position", pos );
1319         [o_time_sld setFloatValue: f_updated];
1320
1321         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1322         [o_time_fld setStringValue: o_time];
1323         [o_fspanel setStreamPos: f_updated andTime: o_time];
1324         vlc_object_release( p_input );
1325     }
1326 }
1327
1328 - (IBAction)volumeAction:(id)sender
1329 {
1330     if (sender == o_volume_sld)
1331         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1332     else if (sender == o_volume_down_btn)
1333         [[VLCCoreInteraction sharedInstance] toggleMute];
1334     else
1335         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1336 }
1337
1338 - (IBAction)effects:(id)sender
1339 {
1340     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1341 }
1342
1343 - (IBAction)fullscreen:(id)sender
1344 {
1345     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1346 }
1347
1348 - (IBAction)dropzoneButtonAction:(id)sender
1349 {
1350     [[[VLCMain sharedInstance] open] openFileGeneric];
1351 }
1352
1353 #pragma mark -
1354 #pragma mark overwritten default functionality
1355 - (BOOL)canBecomeKeyWindow
1356 {
1357     return YES;
1358 }
1359
1360 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1361 {
1362     SEL s_menuAction = [menuItem action];
1363
1364     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1365             return YES;
1366
1367     return [super validateMenuItem:menuItem];
1368 }
1369
1370 - (void)setTitle:(NSString *)title
1371 {
1372     if (b_dark_interface)
1373     {
1374         [o_titlebar_view setWindowTitle: title];
1375         if (b_video_deco)
1376             [o_detached_titlebar_view setWindowTitle: title];
1377     }
1378     if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1379         [o_detached_video_window setTitle: title];
1380     [super setTitle: title];
1381 }
1382
1383 - (void)performClose:(id)sender
1384 {
1385     NSWindow *o_key_window = [NSApp keyWindow];
1386
1387     if (b_dark_interface || !b_video_deco)
1388     {
1389         [o_key_window orderOut: sender];
1390         if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1391             [[VLCCoreInteraction sharedInstance] stop];
1392     }
1393     else
1394     {
1395         if( b_nonembedded && o_key_window != self )
1396             [o_detached_video_window performClose: sender];
1397         else
1398             [super performClose: sender];
1399     }
1400 }
1401
1402 - (void)performMiniaturize:(id)sender
1403 {
1404     if (b_dark_interface)
1405         [self miniaturize: sender];
1406     else
1407         [super performMiniaturize: sender];
1408 }
1409
1410 - (void)performZoom:(id)sender
1411 {
1412     if (b_dark_interface)
1413         [self customZoom: sender];
1414     else
1415         [super performZoom: sender];
1416 }
1417
1418 - (void)zoom:(id)sender
1419 {
1420     if (b_dark_interface)
1421         [self customZoom: sender];
1422     else
1423         [super zoom: sender];
1424 }
1425
1426 /**
1427  * Given a proposed frame rectangle, return a modified version
1428  * which will fit inside the screen.
1429  *
1430  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1431  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1432  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1433  *    Copyright (C) 1996 Free Software Foundation, Inc.
1434  */
1435 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1436 {
1437     NSRect screenRect = [screen visibleFrame];
1438     float difference;
1439
1440     /* Move top edge of the window inside the screen */
1441     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1442     if (difference > 0)
1443     {
1444         frameRect.origin.y -= difference;
1445     }
1446
1447     /* If the window is resizable, resize it (if needed) so that the
1448      bottom edge is on the screen or can be on the screen when the user moves
1449      the window */
1450     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1451     if (_styleMask & NSResizableWindowMask)
1452     {
1453         float difference2;
1454
1455         difference2 = screenRect.origin.y - frameRect.origin.y;
1456         difference2 -= difference;
1457         // Take in account the space between the top of window and the top of the
1458         // screen which can be used to move the bottom of the window on the screen
1459         if (difference2 > 0)
1460         {
1461             frameRect.size.height -= difference2;
1462             frameRect.origin.y += difference2;
1463         }
1464
1465         /* Ensure that resizing doesn't makewindow smaller than minimum */
1466         difference2 = [self minSize].height - frameRect.size.height;
1467         if (difference2 > 0)
1468         {
1469             frameRect.size.height += difference2;
1470             frameRect.origin.y -= difference2;
1471         }
1472     }
1473
1474     return frameRect;
1475 }
1476
1477 #define DIST 3
1478
1479 /**
1480  Zooms the receiver.   This method calls the delegate method
1481  windowShouldZoom:toFrame: to determine if the window should
1482  be allowed to zoom to full screen.
1483  *
1484  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1485  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1486  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1487  *    Copyright (C) 1996 Free Software Foundation, Inc.
1488  */
1489 - (void)customZoom:(id)sender
1490 {
1491     NSRect maxRect = [[self screen] visibleFrame];
1492     NSRect currentFrame = [self frame];
1493
1494     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1495     {
1496         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1497     }
1498
1499     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1500
1501     // Compare the new frame with the current one
1502     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1503         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1504         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1505         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1506     {
1507         // Already in zoomed mode, reset user frame, if stored
1508         if ([self frameAutosaveName] != nil)
1509         {
1510             [self setFrame: previousSavedFrame display: YES animate: YES];
1511             [self saveFrameUsingName: [self frameAutosaveName]];
1512         }
1513         return;
1514     }
1515
1516     if ([self frameAutosaveName] != nil)
1517     {
1518         [self saveFrameUsingName: [self frameAutosaveName]];
1519         previousSavedFrame = [self frame];
1520     }
1521
1522     [self setFrame: maxRect display: YES animate: YES];
1523 }
1524
1525 - (void)windowResizedOrMoved:(NSNotification *)notification
1526 {
1527     [self saveFrameUsingName: [self frameAutosaveName]];
1528 }
1529
1530 - (void)applicationWillTerminate:(NSNotification *)notification
1531 {
1532     [self saveFrameUsingName: [self frameAutosaveName]];
1533 }
1534
1535 - (void)someWindowWillClose:(NSNotification *)notification
1536 {
1537     if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1538     {
1539         if ([[VLCMain sharedInstance] activeVideoPlayback])
1540             [[VLCCoreInteraction sharedInstance] stop];
1541     }
1542 }
1543
1544 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1545 {
1546     if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1547     {
1548         if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1549         {
1550             if([[VLCMain sharedInstance] activeVideoPlayback])
1551                 [[VLCCoreInteraction sharedInstance] pause];
1552         }
1553     }
1554 }
1555
1556 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1557 {
1558     id videoWindow = [o_video_view window];
1559     if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1560         return proposedFrameSize;
1561
1562     // needed when entering lion fullscreen mode
1563     if( b_fullscreen )
1564         return proposedFrameSize;
1565
1566     if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1567     {
1568         NSRect videoWindowFrame = [videoWindow frame];
1569         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1570         NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1571         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1572         float marginx = contentRect.size.width - viewRect.size.width;
1573         if( b_dark_interface && b_video_deco )
1574             marginy += [o_titlebar_view frame].size.height;
1575
1576         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1577     }
1578
1579     return proposedFrameSize;
1580 }
1581
1582 #pragma mark -
1583 #pragma mark Update interface and respond to foreign events
1584 - (void)showDropZone
1585 {
1586     b_dropzone_active = YES;
1587     [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1588     [o_dropzone_view setFrame: [o_playlist_table frame]];
1589     [[o_playlist_table animator] setHidden:YES];
1590 }
1591
1592 - (void)hideDropZone
1593 {
1594     b_dropzone_active = NO;
1595     [o_dropzone_view removeFromSuperview];
1596     [[o_playlist_table animator] setHidden: NO];
1597 }
1598
1599 - (void)hideSplitView
1600 {
1601     NSRect winrect = [self frame];
1602     i_lastSplitViewHeight = [o_split_view frame].size.height;
1603     winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1604     winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1605     [self setFrame: winrect display: YES animate: YES];
1606     [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1607     if (b_dark_interface)
1608     {
1609         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1610         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1611     }
1612     else
1613     {
1614         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1615         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1616     }
1617
1618     b_splitview_removed = YES;
1619 }
1620
1621 - (void)showSplitView
1622 {
1623     [self updateWindow];
1624     if (b_dark_interface)
1625         [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1626     else
1627         [self setContentMinSize:NSMakeSize( 604., 288. )];
1628     [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1629
1630     NSRect winrect;
1631     winrect = [self frame];
1632     winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1633     winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1634     [self setFrame: winrect display: YES animate: YES];
1635
1636     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1637
1638     b_splitview_removed = NO;
1639 }
1640
1641 - (void)updateTimeSlider
1642 {
1643     input_thread_t * p_input;
1644     p_input = pl_CurrentInput( VLCIntf );
1645     if( p_input )
1646     {
1647         NSString * o_time;
1648         vlc_value_t pos;
1649         float f_updated;
1650
1651         var_Get( p_input, "position", &pos );
1652         f_updated = 10000. * pos.f_float;
1653         [o_time_sld setFloatValue: f_updated];
1654
1655         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1656
1657         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1658         if (dur == -1) {
1659             [o_time_sld setEnabled: NO];
1660             [o_time_sld setHidden: YES];
1661             [o_time_sld_fancygradient_view setHidden: YES];
1662         } else {
1663             [o_time_sld setEnabled: YES];
1664             [o_time_sld setHidden: NO];
1665             [o_time_sld_fancygradient_view setHidden: NO];
1666         }
1667
1668         [o_time_fld setStringValue: o_time];
1669         [o_time_fld setNeedsDisplay:YES];
1670         [o_fspanel setStreamPos: f_updated andTime: o_time];
1671         vlc_object_release( p_input );
1672     }
1673     else
1674     {
1675         [o_time_sld setFloatValue: 0.0];
1676         [o_time_fld setStringValue: @"00:00"];
1677         [o_time_sld setEnabled: NO];
1678         [o_time_sld setHidden: YES];
1679         [o_time_sld_fancygradient_view setHidden: YES];
1680         if (b_video_deco)
1681             [o_detached_time_sld_fancygradient_view setHidden: YES];
1682     }
1683
1684     if (b_video_deco)
1685     {
1686         [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1687         [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1688         [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1689         [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1690     }
1691 }
1692
1693 - (void)updateVolumeSlider
1694 {
1695     int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1696     BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1697
1698     if( !b_muted )
1699     {
1700         [o_volume_sld setIntValue: i_volume];
1701         [o_fspanel setVolumeLevel: i_volume];
1702     }
1703     else
1704         [o_volume_sld setIntValue: 0];
1705
1706     [o_volume_sld setEnabled: !b_muted];
1707     [o_volume_up_btn setEnabled: !b_muted];
1708 }
1709
1710 - (void)updateName
1711 {
1712     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1713     input_thread_t * p_input;
1714     p_input = pl_CurrentInput( VLCIntf );
1715     if( p_input )
1716     {
1717         NSString *aString;
1718         char *format = var_InheritString( VLCIntf, "input-title-format" );
1719         char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1720         free( format );
1721         aString = [NSString stringWithUTF8String:formated];
1722         free( formated );
1723
1724         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1725
1726         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1727         if ([o_url isFileURL])
1728         {
1729             [self setRepresentedURL: o_url];
1730             [o_detached_video_window setRepresentedURL: o_url];
1731         } else {
1732             [self setRepresentedURL: nil];
1733             [o_detached_video_window setRepresentedURL: nil];
1734         }
1735         free( uri );
1736
1737         if ([aString isEqualToString:@""])
1738         {
1739             if ([o_url isFileURL])
1740                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1741             else
1742                 aString = [o_url absoluteString];
1743         }
1744
1745         [self setTitle: aString];
1746         [o_fspanel setStreamTitle: aString];
1747         vlc_object_release( p_input );
1748     }
1749     else
1750     {
1751         [self setTitle: _NS("VLC media player")];
1752         [self setRepresentedURL: nil];
1753     }
1754
1755     [o_pool release];
1756 }
1757
1758 - (void)updateWindow
1759 {
1760     bool b_input = false;
1761     bool b_plmul = false;
1762     bool b_control = false;
1763     bool b_seekable = false;
1764     bool b_chapters = false;
1765
1766     playlist_t * p_playlist = pl_Get( VLCIntf );
1767
1768     PL_LOCK;
1769     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1770     PL_UNLOCK;
1771
1772     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1773
1774     bool b_buffering = NO;
1775
1776     if( ( b_input = ( p_input != NULL ) ) )
1777     {
1778         /* seekable streams */
1779         cachedInputState = input_GetState( p_input );
1780         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1781             b_buffering = YES;
1782
1783         /* seekable streams */
1784         b_seekable = var_GetBool( p_input, "can-seek" );
1785
1786         /* check whether slow/fast motion is possible */
1787         b_control = var_GetBool( p_input, "can-rate" );
1788
1789         /* chapters & titles */
1790         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1791
1792         vlc_object_release( p_input );
1793     }
1794
1795     if( b_buffering )
1796     {
1797         [o_progress_bar startAnimation:self];
1798         [o_progress_bar setIndeterminate:YES];
1799         [o_progress_bar setHidden:NO];
1800     } else {
1801         [o_progress_bar stopAnimation:self];
1802         [o_progress_bar setHidden:YES];
1803     }
1804
1805     [o_stop_btn setEnabled: b_input];
1806     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1807     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1808     if (b_show_jump_buttons)
1809     {
1810         [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1811         [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1812     }
1813     if (b_video_deco)
1814     {
1815         [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1816         [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1817     }
1818     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1819
1820     [o_time_sld setEnabled: b_seekable];
1821     [self updateTimeSlider];
1822     [o_fspanel setSeekable: b_seekable];
1823
1824     PL_LOCK;
1825     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1826         [self hideDropZone];
1827     else
1828         [self showDropZone];
1829     PL_UNLOCK;
1830     [o_sidebar_view setNeedsDisplay:YES];
1831 }
1832
1833 - (void)setPause
1834 {
1835     [o_play_btn setImage: o_pause_img];
1836     [o_play_btn setAlternateImage: o_pause_pressed_img];
1837     [o_play_btn setToolTip: _NS("Pause")];
1838     if (b_video_deco)
1839     {
1840         [o_detached_play_btn setImage: o_pause_img];
1841         [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1842         [o_detached_play_btn setToolTip: _NS("Pause")];
1843     }
1844     [o_fspanel setPause];
1845 }
1846
1847 - (void)setPlay
1848 {
1849     [o_play_btn setImage: o_play_img];
1850     [o_play_btn setAlternateImage: o_play_pressed_img];
1851     [o_play_btn setToolTip: _NS("Play")];
1852     if (b_video_deco)
1853     {
1854         [o_detached_play_btn setImage: o_play_img];
1855         [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1856         [o_detached_play_btn setToolTip: _NS("Play")];
1857     }
1858     [o_fspanel setPlay];
1859 }
1860
1861 - (void)drawFancyGradientEffectForTimeSlider
1862 {
1863     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1864     CGFloat f_value = [o_time_sld knobPosition];
1865     if (f_value > 7.5)
1866     {
1867         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1868         if (f_value != oldFrame.size.width)
1869         {
1870             if ([o_time_sld_fancygradient_view isHidden])
1871                 [o_time_sld_fancygradient_view setHidden: NO];
1872             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1873         }
1874
1875         if (b_nonembedded && b_video_deco)
1876         {
1877             f_value = [o_detached_time_sld knobPosition];
1878             oldFrame = [o_detached_time_sld_fancygradient_view frame];
1879             if (f_value != oldFrame.size.width)
1880             {
1881                 if ([o_detached_time_sld_fancygradient_view isHidden])
1882                     [o_detached_time_sld_fancygradient_view setHidden: NO];
1883                 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1884             }
1885         }
1886     }
1887     else
1888     {
1889         NSRect frame;
1890         frame = [o_time_sld_fancygradient_view frame];
1891         if (frame.size.width > 0)
1892         {
1893             frame.size.width = 0;
1894             [o_time_sld_fancygradient_view setFrame: frame];
1895
1896             if (b_video_deco)
1897             {
1898                 frame = [o_detached_time_sld_fancygradient_view frame];
1899                 frame.size.width = 0;
1900                 [o_detached_time_sld_fancygradient_view setFrame: frame];
1901             }
1902         }
1903         [o_time_sld_fancygradient_view setHidden: YES];
1904         if (b_video_deco)
1905             [o_detached_time_sld_fancygradient_view setHidden: YES];
1906     }
1907     [o_pool release];
1908 }
1909
1910 #pragma mark -
1911 #pragma mark Video Output handling
1912 - (id)videoView
1913 {
1914     return o_video_view;
1915 }
1916
1917 - (void)setupVideoView
1918 {
1919     // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1920     if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1921     {
1922         msg_Dbg( VLCIntf, "Creating background window" );
1923         NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1924         if( !screen )
1925             screen = [self screen];
1926         NSRect screen_rect = [screen frame];
1927
1928         if( o_extra_video_window )
1929             [o_extra_video_window release];
1930
1931         o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1932         [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1933         [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1934         [o_extra_video_window setCanBecomeKeyWindow: NO];
1935         [o_extra_video_window setCanBecomeMainWindow: NO];
1936         [o_extra_video_window useOptimizedDrawing: YES];
1937         [o_extra_video_window setMovableByWindowBackground: NO];
1938
1939         [o_video_view retain];
1940         if ([o_video_view superview] != NULL)
1941             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1942         screen_rect.origin.x = screen_rect.origin.y = 0;
1943         [o_video_view setFrame: screen_rect];
1944         [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1945         [o_video_view release];
1946
1947         [o_extra_video_window orderBack:nil];
1948
1949         b_nonembedded = YES;
1950     }
1951     else
1952     {
1953         if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1954         {
1955             if ([o_video_view window] != self)
1956             {
1957                 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1958                 [o_video_view setFrame: [o_split_view frame]];
1959                 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1960             }
1961             b_nonembedded = NO;
1962         }
1963         else
1964         {
1965             if ([o_video_view superview] != NULL)
1966                 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1967
1968             NSRect videoFrame;
1969             videoFrame.size = [[o_detached_video_window contentView] frame].size;
1970             if (b_video_deco)
1971             {
1972                 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1973                 if( b_dark_interface )
1974                     videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1975
1976                 videoFrame.origin.x = .0;
1977                 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1978             }
1979             else
1980             {
1981                 videoFrame.origin.y = .0;
1982                 videoFrame.origin.x = .0;
1983             }
1984
1985             [o_video_view setFrame: videoFrame];
1986             [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1987             [o_detached_video_window setLevel:NSNormalWindowLevel];
1988             [o_detached_video_window useOptimizedDrawing: YES];
1989             b_nonembedded = YES;
1990         }
1991
1992         [[o_video_view window] makeKeyAndOrderFront: self];
1993
1994         vout_thread_t *p_vout = getVout();
1995         if (p_vout)
1996         {
1997             if( var_GetBool( p_vout, "video-on-top" ) )
1998                 [[o_video_view window] setLevel: NSStatusWindowLevel];
1999             else
2000                 [[o_video_view window] setLevel: NSNormalWindowLevel];
2001             vlc_object_release( p_vout );
2002         }
2003     }
2004
2005     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2006 }
2007
2008 - (void)setVideoplayEnabled
2009 {
2010     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
2011
2012     if( b_videoPlayback )
2013     {
2014         // look for 'start at fullscreen'
2015         [[VLCMain sharedInstance] fullscreenChanged];
2016     }
2017     else
2018     {
2019         [self makeFirstResponder: nil];
2020         [o_detached_video_window orderOut: nil];
2021         if( o_extra_video_window )
2022             [o_extra_video_window orderOut: nil];
2023
2024         if( [self level] != NSNormalWindowLevel )
2025             [self setLevel: NSNormalWindowLevel];
2026         if( [o_detached_video_window level] != NSNormalWindowLevel )
2027             [o_detached_video_window setLevel: NSNormalWindowLevel];
2028
2029         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
2030         [self setAlphaValue:1.0];
2031     }
2032
2033     if( b_nativeFullscreenMode )
2034     {
2035         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
2036             [o_bottombar_view setHidden: b_videoPlayback];
2037         else
2038             [o_bottombar_view setHidden: NO];
2039         if( b_videoPlayback && b_fullscreen )
2040             [o_fspanel setActive: nil];
2041         if( !b_videoPlayback )
2042             [o_fspanel setNonActive: nil];
2043     }
2044
2045     if (!b_videoPlayback && b_fullscreen)
2046     {
2047         if (!b_nativeFullscreenMode)
2048             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
2049     }
2050 }
2051
2052 - (void)resizeWindow
2053 {
2054     if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
2055         return;
2056
2057     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
2058     NSSize windowMinSize = [o_videoWindow minSize];
2059     NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
2060
2061     NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
2062     NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
2063
2064     unsigned int i_width = nativeVideoSize.width;
2065     unsigned int i_height = nativeVideoSize.height;
2066     if (i_width < windowMinSize.width)
2067         i_width = windowMinSize.width;
2068     if (i_height < f_min_video_height)
2069         i_height = f_min_video_height;
2070
2071     /* Calculate the window's new size */
2072     NSRect new_frame;
2073     new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
2074     new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
2075     new_frame.origin.x = topleftscreen.x;
2076     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
2077
2078     /* make sure the window doesn't exceed the screen size the window is on */
2079     if( new_frame.size.width > screenFrame.size.width )
2080     {
2081         new_frame.size.width = screenFrame.size.width;
2082         new_frame.origin.x = screenFrame.origin.x;
2083     }
2084     if( new_frame.size.height > screenFrame.size.height )
2085     {
2086         new_frame.size.height = screenFrame.size.height;
2087         new_frame.origin.y = screenFrame.origin.y;
2088     }
2089     if( new_frame.origin.y < screenFrame.origin.y )
2090         new_frame.origin.y = screenFrame.origin.y;
2091
2092     CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
2093     CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
2094     if( right_window_point > right_screen_point )
2095         new_frame.origin.x -= ( right_window_point - right_screen_point );
2096
2097     [[o_videoWindow animator] setFrame:new_frame display:YES];
2098 }
2099
2100 - (void)setNativeVideoSize:(NSSize)size
2101 {
2102     nativeVideoSize = size;
2103
2104     if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
2105         [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2106 }
2107
2108 //  Called automatically if window's acceptsMouseMovedEvents property is true
2109 - (void)mouseMoved:(NSEvent *)theEvent
2110 {
2111     if (b_fullscreen)
2112         [self recreateHideMouseTimer];
2113
2114     [super mouseMoved: theEvent];
2115 }
2116
2117 - (void)recreateHideMouseTimer
2118 {
2119     if (t_hide_mouse_timer != nil) {
2120         [t_hide_mouse_timer invalidate];
2121         [t_hide_mouse_timer release];
2122     }
2123
2124     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2125                                                           target:self
2126                                                         selector:@selector(hideMouseCursor:)
2127                                                         userInfo:nil
2128                                                          repeats:NO];
2129     [t_hide_mouse_timer retain];
2130 }
2131
2132 //  NSTimer selectors require this function signature as per Apple's docs
2133 - (void)hideMouseCursor:(NSTimer *)timer
2134 {
2135     [NSCursor setHiddenUntilMouseMoves: YES];
2136 }
2137
2138 #pragma mark -
2139 #pragma mark Fullscreen support
2140 - (void)showFullscreenController
2141 {
2142      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2143         [o_fspanel fadeIn];
2144 }
2145
2146 - (BOOL)fullscreen
2147 {
2148     return b_fullscreen;
2149 }
2150
2151 - (void)lockFullscreenAnimation
2152 {
2153     [o_animation_lock lock];
2154 }
2155
2156 - (void)unlockFullscreenAnimation
2157 {
2158     [o_animation_lock unlock];
2159 }
2160
2161 - (void)enterFullscreen
2162 {
2163     NSMutableDictionary *dict1, *dict2;
2164     NSScreen *screen;
2165     NSRect screen_rect;
2166     NSRect rect;
2167     BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2168     o_current_video_window = [o_video_view window];
2169
2170     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2171     [self lockFullscreenAnimation];
2172
2173     if (!screen)
2174     {
2175         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2176         screen = [o_current_video_window screen];
2177     }
2178     if (!screen)
2179     {
2180         msg_Dbg( VLCIntf, "Using deepest screen" );
2181         screen = [NSScreen deepestScreen];
2182     }
2183
2184     screen_rect = [screen frame];
2185
2186     [o_fullscreen_btn setState: YES];
2187     if (b_video_deco)
2188         [o_detached_fullscreen_btn setState: YES];
2189
2190     [self recreateHideMouseTimer];
2191
2192     if( blackout_other_displays )
2193         [screen blackoutOtherScreens];
2194
2195     /* Make sure we don't see the window flashes in float-on-top mode */
2196     i_originalLevel = [o_current_video_window level];
2197     [o_current_video_window setLevel:NSNormalWindowLevel];
2198
2199     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2200     if (!o_fullscreen_window)
2201     {
2202         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2203
2204         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2205         rect.origin.x += [o_current_video_window frame].origin.x;
2206         rect.origin.y += [o_current_video_window frame].origin.y;
2207         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2208         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2209         [o_fullscreen_window setCanBecomeKeyWindow: YES];
2210         [o_fullscreen_window setCanBecomeMainWindow: YES];
2211
2212         if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2213         {
2214             /* We don't animate if we are not visible, instead we
2215              * simply fade the display */
2216             CGDisplayFadeReservationToken token;
2217
2218             if( blackout_other_displays )
2219             {
2220                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2221                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2222             }
2223
2224             if ([screen mainScreen])
2225                 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2226
2227             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2228             [o_temp_view setFrame:[o_video_view frame]];
2229             [o_fullscreen_window setContentView:o_video_view];
2230
2231             [o_fullscreen_window makeKeyAndOrderFront:self];
2232             [o_fullscreen_window orderFront:self animate:YES];
2233
2234             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2235             [o_fullscreen_window setLevel:NSNormalWindowLevel];
2236
2237             if( blackout_other_displays )
2238             {
2239                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2240                 CGReleaseDisplayFadeReservation( token );
2241             }
2242
2243             /* Will release the lock */
2244             [self hasBecomeFullscreen];
2245
2246             return;
2247         }
2248
2249         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2250         NSDisableScreenUpdates();
2251         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2252         [o_temp_view setFrame:[o_video_view frame]];
2253         [o_fullscreen_window setContentView:o_video_view];
2254         [o_fullscreen_window makeKeyAndOrderFront:self];
2255         NSEnableScreenUpdates();
2256     }
2257
2258     /* We are in fullscreen (and no animation is running) */
2259     if (b_fullscreen)
2260     {
2261         /* Make sure we are hidden */
2262         [o_current_video_window orderOut: self];
2263
2264         [self unlockFullscreenAnimation];
2265         return;
2266     }
2267
2268     if (o_fullscreen_anim1)
2269     {
2270         [o_fullscreen_anim1 stopAnimation];
2271         [o_fullscreen_anim1 release];
2272     }
2273     if (o_fullscreen_anim2)
2274     {
2275         [o_fullscreen_anim2 stopAnimation];
2276         [o_fullscreen_anim2 release];
2277     }
2278
2279     if ([screen mainScreen])
2280         [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2281
2282     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2283     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2284
2285     [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2286     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2287
2288     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2289     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2290     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2291
2292     /* Strategy with NSAnimation allocation:
2293      - Keep at most 2 animation at a time
2294      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2295      */
2296     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2297     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2298
2299     [dict1 release];
2300     [dict2 release];
2301
2302     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2303     [o_fullscreen_anim1 setDuration: 0.3];
2304     [o_fullscreen_anim1 setFrameRate: 30];
2305     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2306     [o_fullscreen_anim2 setDuration: 0.2];
2307     [o_fullscreen_anim2 setFrameRate: 30];
2308
2309     [o_fullscreen_anim2 setDelegate: self];
2310     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2311
2312     [o_fullscreen_anim1 startAnimation];
2313     /* fullscreenAnimation will be unlocked when animation ends */
2314 }
2315
2316 - (void)hasBecomeFullscreen
2317 {
2318     if( [[o_video_view subviews] count] > 0 )
2319         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2320
2321     [o_fullscreen_window makeKeyWindow];
2322     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2323
2324     /* tell the fspanel to move itself to front next time it's triggered */
2325     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2326     [o_fspanel setActive: nil];
2327
2328     if( [o_current_video_window isVisible] )
2329         [o_current_video_window orderOut: self];
2330
2331     b_fullscreen = YES;
2332     [self unlockFullscreenAnimation];
2333 }
2334
2335 - (void)leaveFullscreen
2336 {
2337     [self leaveFullscreenAndFadeOut: NO];
2338 }
2339
2340 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2341 {
2342     NSMutableDictionary *dict1, *dict2;
2343     NSRect frame;
2344     BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2345
2346     if( !o_current_video_window )
2347         return;
2348
2349     [self lockFullscreenAnimation];
2350
2351     [o_fullscreen_btn setState: NO];
2352     if (b_video_deco)
2353         [o_detached_fullscreen_btn setState: NO];
2354
2355     /* We always try to do so */
2356     [NSScreen unblackoutScreens];
2357
2358     vout_thread_t *p_vout = getVout();
2359     if (p_vout)
2360     {
2361         if( var_GetBool( p_vout, "video-on-top" ) )
2362             [[o_video_view window] setLevel: NSStatusWindowLevel];
2363         else
2364             [[o_video_view window] setLevel: NSNormalWindowLevel];
2365         vlc_object_release( p_vout );
2366     }
2367     [[o_video_view window] makeKeyAndOrderFront: nil];
2368
2369     /* Don't do anything if o_fullscreen_window is already closed */
2370     if (!o_fullscreen_window)
2371     {
2372         [self unlockFullscreenAnimation];
2373         return;
2374     }
2375
2376     if (fadeout)
2377     {
2378         /* We don't animate if we are not visible, instead we
2379          * simply fade the display */
2380         CGDisplayFadeReservationToken token;
2381
2382         if( blackout_other_displays )
2383         {
2384             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2385             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2386         }
2387
2388         [o_fspanel setNonActive: nil];
2389         [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2390
2391         /* Will release the lock */
2392         [self hasEndedFullscreen];
2393
2394         /* Our window is hidden, and might be faded. We need to workaround that, so note it
2395          * here */
2396         b_window_is_invisible = YES;
2397
2398         if( blackout_other_displays )
2399         {
2400             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2401             CGReleaseDisplayFadeReservation( token );
2402         }
2403
2404         return;
2405     }
2406
2407     [o_current_video_window setAlphaValue: 0.0];
2408     [o_current_video_window orderFront: self];
2409     [[o_video_view window] orderFront: self];
2410
2411     [o_fspanel setNonActive: nil];
2412     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2413
2414     if (o_fullscreen_anim1)
2415     {
2416         [o_fullscreen_anim1 stopAnimation];
2417         [o_fullscreen_anim1 release];
2418     }
2419     if (o_fullscreen_anim2)
2420     {
2421         [o_fullscreen_anim2 stopAnimation];
2422         [o_fullscreen_anim2 release];
2423     }
2424
2425     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2426     frame.origin.x += [o_current_video_window frame].origin.x;
2427     frame.origin.y += [o_current_video_window frame].origin.y;
2428
2429     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2430     [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2431     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2432
2433     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2434     [dict2 release];
2435
2436     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2437     [o_fullscreen_anim2 setDuration: 0.3];
2438     [o_fullscreen_anim2 setFrameRate: 30];
2439
2440     [o_fullscreen_anim2 setDelegate: self];
2441
2442     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2443
2444     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2445     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2446     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2447
2448     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2449     [dict1 release];
2450
2451     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2452     [o_fullscreen_anim1 setDuration: 0.2];
2453     [o_fullscreen_anim1 setFrameRate: 30];
2454     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2455
2456     /* Make sure o_fullscreen_window is the frontmost window */
2457     [o_fullscreen_window orderFront: self];
2458
2459     [o_fullscreen_anim1 startAnimation];
2460     /* fullscreenAnimation will be unlocked when animation ends */
2461 }
2462
2463 - (void)hasEndedFullscreen
2464 {
2465     b_fullscreen = NO;
2466
2467     /* This function is private and should be only triggered at the end of the fullscreen change animation */
2468     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2469     NSDisableScreenUpdates();
2470     [o_video_view retain];
2471     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2472     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2473     [o_video_view release];
2474     [o_video_view setFrame:[o_temp_view frame]];
2475     if( [[o_video_view subviews] count] > 0 )
2476         [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2477     if( !b_nonembedded )
2478             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2479     else
2480         [[o_video_view window] makeKeyAndOrderFront: self];
2481     [o_fullscreen_window orderOut: self];
2482     NSEnableScreenUpdates();
2483
2484     [o_fullscreen_window release];
2485     o_fullscreen_window = nil;
2486     [[o_video_view window] setLevel:i_originalLevel];
2487     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2488
2489     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2490     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2491         [o_current_video_window orderOut: self];
2492
2493     o_current_video_window = nil;
2494     [self unlockFullscreenAnimation];
2495 }
2496
2497 - (void)animationDidEnd:(NSAnimation*)animation
2498 {
2499     NSArray *viewAnimations;
2500     if( o_makekey_anim == animation )
2501     {
2502         [o_makekey_anim release];
2503         return;
2504     }
2505     if ([animation currentValue] < 1.0)
2506         return;
2507
2508     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2509     viewAnimations = [o_fullscreen_anim2 viewAnimations];
2510     if ([viewAnimations count] >=1 &&
2511         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2512     {
2513         /* Fullscreen ended */
2514         [self hasEndedFullscreen];
2515     }
2516     else
2517     {
2518         /* Fullscreen started */
2519         [self hasBecomeFullscreen];
2520     }
2521 }
2522
2523 - (void)makeKeyAndOrderFront: (id)sender
2524 {
2525     /* Hack
2526      * when we exit fullscreen and fade out, we may endup in
2527      * having a window that is faded. We can't have it fade in unless we
2528      * animate again. */
2529
2530     if(!b_window_is_invisible)
2531     {
2532         /* Make sure we don't do it too much */
2533         [super makeKeyAndOrderFront: sender];
2534         return;
2535     }
2536
2537     [super setAlphaValue:0.0f];
2538     [super makeKeyAndOrderFront: sender];
2539
2540     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2541     [dict setObject:self forKey:NSViewAnimationTargetKey];
2542     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2543
2544     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2545     [dict release];
2546
2547     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2548     [o_makekey_anim setDuration: 0.1];
2549     [o_makekey_anim setFrameRate: 30];
2550     [o_makekey_anim setDelegate: self];
2551
2552     [o_makekey_anim startAnimation];
2553     b_window_is_invisible = NO;
2554
2555     /* fullscreenAnimation will be unlocked when animation ends */
2556 }
2557
2558 #pragma mark -
2559 #pragma mark Lion native fullscreen handling
2560 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2561 {
2562     // workaround, see #6668
2563     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2564
2565     var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2566
2567     vout_thread_t *p_vout = getVout();
2568     if( p_vout )
2569     {
2570         var_SetBool( p_vout, "fullscreen", true );
2571         vlc_object_release( p_vout );
2572     }
2573
2574     [o_video_view setFrame: [[self contentView] frame]];
2575     b_fullscreen = YES;
2576
2577     [self recreateHideMouseTimer];
2578     i_originalLevel = [self level];
2579     [self setLevel:NSNormalWindowLevel];
2580
2581     if (b_dark_interface)
2582     {
2583         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2584
2585         NSRect winrect;
2586         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2587         winrect = [self frame];
2588
2589         winrect.size.height = winrect.size.height - f_titleBarHeight;
2590         [self setFrame: winrect display:NO animate:NO];
2591         winrect = [o_split_view frame];
2592         winrect.size.height = winrect.size.height + f_titleBarHeight;
2593         [o_split_view setFrame: winrect];
2594     }
2595
2596     if ([[VLCMain sharedInstance] activeVideoPlayback])
2597         [o_bottombar_view setHidden: YES];
2598
2599     [self setMovableByWindowBackground: NO];
2600 }
2601
2602 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2603 {
2604     // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2605     // But this creates some problems when leaving fs over remote intfs, so activate app here.
2606     [NSApp activateIgnoringOtherApps:YES];
2607
2608     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2609     [o_fspanel setActive: nil];
2610 }
2611
2612 - (void)windowWillExitFullScreen:(NSNotification *)notification
2613 {
2614
2615     var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2616
2617     vout_thread_t *p_vout = getVout();
2618     if( p_vout )
2619     {
2620         var_SetBool( p_vout, "fullscreen", false );
2621         vlc_object_release( p_vout );
2622     }
2623
2624     [o_video_view setFrame: [o_split_view frame]];
2625     [NSCursor setHiddenUntilMouseMoves: NO];
2626     [o_fspanel setNonActive: nil];
2627     [self setLevel:i_originalLevel];
2628     b_fullscreen = NO;
2629
2630     if (b_dark_interface)
2631     {
2632         NSRect winrect;
2633         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2634         winrect = [self frame];
2635
2636         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2637                                               winrect.size.width, f_titleBarHeight )];
2638         [[self contentView] addSubview: o_titlebar_view];
2639
2640         winrect.size.height = winrect.size.height + f_titleBarHeight;
2641         [self setFrame: winrect display:NO animate:NO];
2642         winrect = [o_split_view frame];
2643         winrect.size.height = winrect.size.height - f_titleBarHeight;
2644         [o_split_view setFrame: winrect];
2645         [o_video_view setFrame: winrect];
2646     }
2647
2648     if ([[VLCMain sharedInstance] activeVideoPlayback])
2649         [o_bottombar_view setHidden: NO];
2650
2651     [self setMovableByWindowBackground: YES];
2652 }
2653
2654 #pragma mark -
2655 #pragma mark split view delegate
2656 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2657 {
2658     if (dividerIndex == 0)
2659         return 300.;
2660     else
2661         return proposedMax;
2662 }
2663
2664 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2665 {
2666     if (dividerIndex == 0)
2667         return 100.;
2668     else
2669         return proposedMin;
2670 }
2671
2672 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2673 {
2674     return ([subview isEqual:o_left_split_view]);
2675 }
2676
2677 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2678 {
2679     if ([subview isEqual:o_left_split_view])
2680         return NO;
2681     return YES;
2682 }
2683
2684 #pragma mark -
2685 #pragma mark Side Bar Data handling
2686 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2687 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2688 {
2689     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2690     if(item==nil)
2691         return [o_sidebaritems count];
2692     else
2693         return [[item children] count];
2694 }
2695
2696
2697 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2698 {
2699     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2700     if(item==nil)
2701         return [o_sidebaritems objectAtIndex:index];
2702     else
2703         return [[item children] objectAtIndex:index];
2704 }
2705
2706
2707 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2708 {
2709     return [item title];
2710 }
2711
2712 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2713 {
2714     [item setTitle:object];
2715 }
2716
2717 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2718 {
2719     return [item hasChildren];
2720 }
2721
2722
2723 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2724 {
2725     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2726         return YES;
2727
2728     return [item hasBadge];
2729 }
2730
2731
2732 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2733 {
2734     playlist_t * p_playlist = pl_Get( VLCIntf );
2735     NSInteger i_playlist_size;
2736
2737     if ([[item identifier] isEqualToString: @"playlist"])
2738     {
2739         PL_LOCK;
2740         i_playlist_size = p_playlist->p_local_category->i_children;
2741         PL_UNLOCK;
2742
2743         return i_playlist_size;
2744     }
2745     if ([[item identifier] isEqualToString: @"medialibrary"])
2746     {
2747         PL_LOCK;
2748         i_playlist_size = p_playlist->p_ml_category->i_children;
2749         PL_UNLOCK;
2750
2751         return i_playlist_size;
2752     }
2753
2754     return [item badgeValue];
2755 }
2756
2757
2758 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2759 {
2760     return [item hasIcon];
2761 }
2762
2763
2764 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2765 {
2766     return [item icon];
2767 }
2768
2769 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2770 {
2771     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2772     {
2773         if (item != nil)
2774         {
2775             NSMenu * m;
2776             if ([item sdtype] > 0)
2777             {
2778                 m = [[NSMenu alloc] init];
2779                 playlist_t * p_playlist = pl_Get( VLCIntf );
2780                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2781                 if (!sd_loaded)
2782                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2783                 else
2784                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2785                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2786             }
2787             return [m autorelease];
2788         }
2789     }
2790
2791     return nil;
2792 }
2793
2794 - (IBAction)sdmenuhandler:(id)sender
2795 {
2796     NSString * identifier = [sender representedObject];
2797     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2798     {
2799         playlist_t * p_playlist = pl_Get( VLCIntf );
2800         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2801
2802         if (!sd_loaded)
2803             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2804         else
2805             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2806     }
2807 }
2808
2809 #pragma mark -
2810 #pragma mark Side Bar Delegate Methods
2811 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2812 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2813 {
2814     if ([[group identifier] isEqualToString:@"library"])
2815         return YES;
2816
2817     return NO;
2818 }
2819
2820 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2821 {
2822     playlist_t * p_playlist = pl_Get( VLCIntf );
2823
2824     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2825     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2826
2827
2828     //Set the label text to represent the new selection
2829     if ([item sdtype] > -1 && [[item identifier] length] > 0)
2830     {
2831         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2832         if (!sd_loaded)
2833         {
2834             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2835         }
2836     }
2837
2838     [o_chosen_category_lbl setStringValue:[item title]];
2839
2840     if ([[item identifier] isEqualToString:@"playlist"])
2841     {
2842         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2843     }
2844     else if([[item identifier] isEqualToString:@"medialibrary"])
2845     {
2846         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2847     }
2848     else
2849     {
2850         playlist_item_t * pl_item;
2851         PL_LOCK;
2852         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2853         PL_UNLOCK;
2854         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2855     }
2856
2857     PL_LOCK;
2858     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2859         [self hideDropZone];
2860     else
2861         [self showDropZone];
2862     PL_UNLOCK;
2863
2864     if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
2865         [self showPodcastControls];
2866     else
2867         [self hidePodcastControls];
2868 }
2869
2870 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2871 {
2872     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2873     {
2874         NSPasteboard *o_pasteboard = [info draggingPasteboard];
2875         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2876             return NSDragOperationGeneric;
2877     }
2878     return NSDragOperationNone;
2879 }
2880
2881 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2882 {
2883     NSPasteboard *o_pasteboard = [info draggingPasteboard];
2884
2885     playlist_t * p_playlist = pl_Get( VLCIntf );
2886     playlist_item_t *p_node;
2887
2888     if ([[item identifier] isEqualToString:@"playlist"])
2889         p_node = p_playlist->p_local_category;
2890     else
2891         p_node = p_playlist->p_ml_category;
2892
2893     if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2894     {
2895         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2896         NSUInteger count = [o_values count];
2897         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2898
2899         for( NSUInteger i = 0; i < count; i++)
2900         {
2901             NSDictionary *o_dic;
2902             char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2903             if( !psz_uri )
2904                 continue;
2905
2906             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2907
2908             free( psz_uri );
2909
2910             [o_array addObject: o_dic];
2911         }
2912
2913         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2914         return YES;
2915     }
2916     else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2917     {
2918         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2919
2920         NSUInteger count = [array count];
2921         playlist_item_t * p_item = NULL;
2922
2923         PL_LOCK;
2924         for( NSUInteger i = 0; i < count; i++ )
2925         {
2926             p_item = [[array objectAtIndex:i] pointerValue];
2927             if( !p_item ) continue;
2928             playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2929         }
2930         PL_UNLOCK;
2931
2932         return YES;
2933     }
2934     return NO;
2935 }
2936
2937 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2938 {
2939     return [item identifier];
2940 }
2941
2942 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2943 {
2944     /* the following code assumes for sakes of simplicity that only the top level
2945      * items are allowed to have children */
2946
2947     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2948     NSUInteger count = [array count];
2949     if (count < 1)
2950         return nil;
2951
2952     for (NSUInteger x = 0; x < count; x++)
2953     {
2954         id item = [array objectAtIndex: x]; // save one objc selector call
2955         if ([[item identifier] isEqualToString:object])
2956             return item;
2957     }
2958
2959     return nil;
2960 }
2961
2962 #pragma mark -
2963 #pragma mark Podcast
2964
2965 - (IBAction)addPodcast:(id)sender
2966 {
2967     [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2968 }
2969
2970 - (IBAction)addPodcastWindowAction:(id)sender
2971 {
2972     [o_podcast_subscribe_window orderOut:sender];
2973     [NSApp endSheet: o_podcast_subscribe_window];
2974
2975     if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
2976         NSMutableString * podcastConf = [[NSMutableString alloc] init];
2977         if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL)
2978             [podcastConf appendFormat:@"%s|", config_GetPsz( VLCIntf, "podcast-urls" )];
2979
2980         [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
2981         config_PutPsz( VLCIntf, "podcast-urls", [podcastConf UTF8String] );
2982
2983         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2984         if( p_obj ) {
2985             var_SetString( p_obj, "podcast-urls", [podcastConf UTF8String] );
2986             vlc_object_release( p_obj );
2987         }
2988         [podcastConf release];
2989     }
2990 }
2991
2992 - (IBAction)removePodcast:(id)sender
2993 {
2994     if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL) {
2995         [o_podcast_unsubscribe_pop removeAllItems];
2996         [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2997         [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2998     }
2999 }
3000
3001 - (IBAction)removePodcastWindowAction:(id)sender
3002 {
3003     [o_podcast_unsubscribe_window orderOut:sender];
3004     [NSApp endSheet: o_podcast_unsubscribe_window];
3005
3006     if (sender == o_podcast_unsubscribe_ok_btn) {
3007         NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
3008         [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
3009         config_PutPsz( VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String] );
3010         [urls release];
3011
3012         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
3013         if( p_obj ) {
3014             var_SetString( p_obj, "podcast-urls", config_GetPsz( VLCIntf, "podcast-urls" ) );
3015             vlc_object_release( p_obj );
3016         }
3017
3018         /* reload the podcast module, since it won't update its list when removing podcasts */
3019         playlist_t * p_playlist = pl_Get( VLCIntf );
3020         if( playlist_IsServicesDiscoveryLoaded( p_playlist, "podcast{longname=\"Podcasts\"}" ) ) {
3021             playlist_ServicesDiscoveryRemove( p_playlist, "podcast{longname=\"Podcasts\"}" );
3022             playlist_ServicesDiscoveryAdd( p_playlist, "podcast{longname=\"Podcasts\"}" );
3023             [o_playlist_table reloadData];
3024         }
3025
3026     }
3027 }
3028
3029 - (void)showPodcastControls
3030 {
3031     NSRect podcastViewDimensions = [o_podcast_view frame];
3032     NSRect rightSplitRect = [o_right_split_view frame];
3033     NSRect playlistTableRect = [o_playlist_table frame];
3034
3035     podcastViewDimensions.size.width = rightSplitRect.size.width;
3036     podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
3037     [o_podcast_view setFrame:podcastViewDimensions];
3038
3039     playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
3040     playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
3041     [o_playlist_table setFrame:playlistTableRect];
3042     [o_playlist_table setNeedsDisplay:YES];
3043
3044     [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
3045     b_podcastView_displayed = YES;
3046 }
3047
3048 - (void)hidePodcastControls
3049 {
3050     if (b_podcastView_displayed) {
3051         NSRect podcastViewDimensions = [o_podcast_view frame];
3052         NSRect playlistTableRect = [o_playlist_table frame];
3053
3054         playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
3055         playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
3056
3057         [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
3058         [o_playlist_table setFrame: playlistTableRect];
3059         b_podcastView_displayed = NO;
3060     }
3061 }
3062
3063 #pragma mark -
3064 #pragma mark Accessibility stuff
3065
3066 - (NSArray *)accessibilityAttributeNames
3067 {
3068     if( !b_dark_interface )
3069         return [super accessibilityAttributeNames];
3070
3071     static NSMutableArray *attributes = nil;
3072     if ( attributes == nil ) {
3073         attributes = [[super accessibilityAttributeNames] mutableCopy];
3074         NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3075                                      NSAccessibilityCloseButtonAttribute,
3076                                      NSAccessibilityMinimizeButtonAttribute,
3077                                      NSAccessibilityZoomButtonAttribute,
3078                                      nil];
3079
3080         for( NSString *attribute in appendAttributes )
3081         {
3082             if( ![attributes containsObject:attribute] )
3083                 [attributes addObject:attribute];
3084         }
3085     }
3086     return attributes;
3087 }
3088
3089 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3090 {
3091     if( b_dark_interface )
3092     {
3093         VLCMainWindowTitleView *o_tbv = o_titlebar_view;
3094
3095         if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3096             return NSAccessibilityStandardWindowSubrole;
3097
3098         if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3099             return [[o_tbv closeButton] cell];
3100
3101         if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3102             return [[o_tbv minimizeButton] cell];
3103
3104         if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3105             return [[o_tbv zoomButton] cell];
3106     }
3107
3108     return [super accessibilityAttributeValue: o_attribute_name];
3109 }
3110
3111 - (id)detachedTitlebarView
3112 {
3113     return o_detached_titlebar_view;
3114 }
3115 @end
3116
3117 @implementation VLCDetachedVideoWindow
3118
3119 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
3120                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
3121 {
3122     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
3123     b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
3124
3125     if (b_dark_interface || !b_video_deco)
3126     {
3127 #ifdef MAC_OS_X_VERSION_10_7
3128         if (!OSX_SNOW_LEOPARD)
3129             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
3130         else
3131             styleMask = NSBorderlessWindowMask;
3132 #else
3133         styleMask = NSBorderlessWindowMask;
3134 #endif
3135     }
3136
3137     self = [super initWithContentRect:contentRect styleMask:styleMask
3138                                   backing:backingType defer:flag];
3139
3140     /* we want to be moveable regardless of our style */
3141     [self setMovableByWindowBackground: YES];
3142
3143     /* we don't want this window to be restored on relaunch */
3144     if (!OSX_SNOW_LEOPARD)
3145         [self setRestorable:NO];
3146
3147     return self;
3148 }
3149
3150 - (void)awakeFromNib
3151 {
3152     [self setAcceptsMouseMovedEvents: YES];
3153
3154     if (b_dark_interface)
3155     {
3156         [self setBackgroundColor: [NSColor clearColor]];
3157         [self setOpaque: NO];
3158         [self display];
3159         [self setHasShadow:NO];
3160         [self setHasShadow:YES];
3161     }
3162 }
3163
3164 - (IBAction)fullscreen:(id)sender
3165 {
3166     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
3167 }
3168
3169 - (void)performClose:(id)sender
3170 {
3171     if (b_dark_interface || !b_video_deco)
3172         [[VLCMainWindow sharedInstance] performClose: sender];
3173     else
3174         [super performClose: sender];
3175 }
3176
3177 - (void)performMiniaturize:(id)sender
3178 {
3179     if (b_dark_interface || !b_video_deco)
3180         [self miniaturize: sender];
3181     else
3182         [super performMiniaturize: sender];
3183 }
3184
3185 - (void)performZoom:(id)sender
3186 {
3187     if (b_dark_interface || !b_video_deco)
3188         [self customZoom: sender];
3189     else
3190         [super performZoom: sender];
3191 }
3192
3193 - (void)zoom:(id)sender
3194 {
3195     if (b_dark_interface || !b_video_deco)
3196         [self customZoom: sender];
3197     else
3198         [super zoom: sender];
3199 }
3200
3201 - (BOOL)canBecomeKeyWindow
3202 {
3203     return YES;
3204 }
3205
3206 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
3207 {
3208     SEL s_menuAction = [menuItem action];
3209
3210     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3211         return YES;
3212
3213     return [super validateMenuItem:menuItem];
3214 }
3215
3216 /**
3217  * Given a proposed frame rectangle, return a modified version
3218  * which will fit inside the screen.
3219  *
3220  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3221  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3222  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3223  *    Copyright (C) 1996 Free Software Foundation, Inc.
3224  */
3225 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3226 {
3227     NSRect screenRect = [screen visibleFrame];
3228     float difference;
3229
3230     /* Move top edge of the window inside the screen */
3231     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3232     if (difference > 0)
3233     {
3234         frameRect.origin.y -= difference;
3235     }
3236
3237     /* If the window is resizable, resize it (if needed) so that the
3238      bottom edge is on the screen or can be on the screen when the user moves
3239      the window */
3240     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3241     if (_styleMask & NSResizableWindowMask)
3242     {
3243         float difference2;
3244
3245         difference2 = screenRect.origin.y - frameRect.origin.y;
3246         difference2 -= difference;
3247         // Take in account the space between the top of window and the top of the
3248         // screen which can be used to move the bottom of the window on the screen
3249         if (difference2 > 0)
3250         {
3251             frameRect.size.height -= difference2;
3252             frameRect.origin.y += difference2;
3253         }
3254
3255         /* Ensure that resizing doesn't makewindow smaller than minimum */
3256         difference2 = [self minSize].height - frameRect.size.height;
3257         if (difference2 > 0)
3258         {
3259             frameRect.size.height += difference2;
3260             frameRect.origin.y -= difference2;
3261         }
3262     }
3263
3264     return frameRect;
3265 }
3266
3267 #define DIST 3
3268
3269 /**
3270  Zooms the receiver.   This method calls the delegate method
3271  windowShouldZoom:toFrame: to determine if the window should
3272  be allowed to zoom to full screen.
3273  *
3274  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3275  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3276  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3277  *    Copyright (C) 1996 Free Software Foundation, Inc.
3278  */
3279 - (void) customZoom: (id)sender
3280 {
3281     NSRect maxRect = [[self screen] visibleFrame];
3282     NSRect currentFrame = [self frame];
3283
3284     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3285     {
3286         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3287     }
3288
3289     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3290
3291     // Compare the new frame with the current one
3292     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3293         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3294         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3295         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3296     {
3297         // Already in zoomed mode, reset user frame, if stored
3298         if ([self frameAutosaveName] != nil)
3299         {
3300             [self setFrame: previousSavedFrame display: YES animate: YES];
3301             [self saveFrameUsingName: [self frameAutosaveName]];
3302         }
3303         return;
3304     }
3305
3306     if ([self frameAutosaveName] != nil)
3307     {
3308         [self saveFrameUsingName: [self frameAutosaveName]];
3309         previousSavedFrame = [self frame];
3310     }
3311
3312     [self setFrame: maxRect display: YES animate: YES];
3313 }
3314
3315 - (NSArray *)accessibilityAttributeNames
3316 {
3317     if( !b_dark_interface )
3318         return [super accessibilityAttributeNames];
3319
3320     static NSMutableArray *attributes = nil;
3321     if ( attributes == nil ) {
3322         attributes = [[super accessibilityAttributeNames] mutableCopy];
3323         NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3324                                      NSAccessibilityCloseButtonAttribute,
3325                                      NSAccessibilityMinimizeButtonAttribute,
3326                                      NSAccessibilityZoomButtonAttribute,
3327                                      nil];
3328
3329         for( NSString *attribute in appendAttributes )
3330         {
3331             if( ![attributes containsObject:attribute] )
3332                 [attributes addObject:attribute];
3333         }
3334     }
3335     return attributes;
3336 }
3337
3338 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3339 {
3340     if( b_dark_interface )
3341     {
3342         VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3343
3344         if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3345             return NSAccessibilityStandardWindowSubrole;
3346
3347         if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3348             return [[o_tbv closeButton] cell];
3349
3350         if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3351             return [[o_tbv minimizeButton] cell];
3352
3353         if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3354             return [[o_tbv zoomButton] cell];
3355     }
3356
3357     return [super accessibilityAttributeValue: o_attribute_name];
3358 }
3359
3360 @end