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