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