]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: replaced previous/next buttons for the dark UI
[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_LION)
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_LION)
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_LION && 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:@"back"]];
272         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
273         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back"]];
274         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-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"]];
280         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
281         [o_detached_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
282         [o_detached_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-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:@"back-3btns-dark"]];
324         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-3btns-dark-pressed"]];
325         [o_detached_bwd_btn setImage: [NSImage imageNamed:@"back-3btns-dark"]];
326         [o_detached_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-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_LION)
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_LION)
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     // FIXME: switch o_playlist_btn artwork
882
883     #define resizeItem( item ) \
884     frame = [item frame]; \
885     frame.size.width = frame.size.width - f_space; \
886     frame.origin.x = frame.origin.x + f_space; \
887     if( b_fast ) \
888         [item setFrame: frame]; \
889     else \
890         [[item animator] setFrame: frame]
891
892     resizeItem( o_time_sld );
893     resizeItem( o_progress_bar );
894     resizeItem( o_time_sld_background );
895     resizeItem( o_time_sld_fancygradient_view );
896     #undef resizeItem
897
898     if (b_fast) {
899         [o_repeat_btn setHidden: NO];
900         [o_shuffle_btn setHidden: NO];
901     } else {
902         [[o_repeat_btn animator] setHidden: NO];
903         [[o_shuffle_btn animator] setHidden: NO];
904     }
905 }
906
907 - (void)removePlaymodeButtons:(BOOL)b_fast
908 {
909     NSRect frame;
910     float f_space = [o_repeat_btn frame].size.width + [o_shuffle_btn frame].size.width - 6.;
911     [o_repeat_btn setHidden: YES];
912     [o_shuffle_btn setHidden: YES];
913
914     // FIXME: switch o_playlist_btn artwork
915
916     #define resizeItem( item ) \
917     frame = [item frame]; \
918     frame.size.width = frame.size.width + f_space; \
919     frame.origin.x = frame.origin.x - f_space; \
920     if( b_fast ) \
921         [item setFrame: frame]; \
922     else \
923         [[item animator] setFrame: frame]
924
925     resizeItem( o_time_sld );
926     resizeItem( o_progress_bar );
927     resizeItem( o_time_sld_background );
928     resizeItem( o_time_sld_fancygradient_view );
929     #undef resizeItem
930 }
931
932 #pragma mark -
933 #pragma mark Button Actions
934
935 - (IBAction)play:(id)sender
936 {
937     [[VLCCoreInteraction sharedInstance] play];
938 }
939
940 - (void)resetPreviousButton
941 {
942     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
943         // seems like no further event occurred, so let's switch the playback item
944         [[VLCCoreInteraction sharedInstance] previous];
945         just_triggered_previous = NO;
946     }
947 }
948
949 - (void)resetBackwardSkip
950 {
951     // the user stopped skipping, so let's allow him to change the item
952     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
953         just_triggered_previous = NO;
954 }
955
956 - (IBAction)prev:(id)sender
957 {
958     [[VLCCoreInteraction sharedInstance] previous];
959 }
960
961 - (IBAction)bwd:(id)sender
962 {
963     if(!just_triggered_previous)
964     {
965         just_triggered_previous = YES;
966         [self performSelector:@selector(resetPreviousButton)
967                    withObject: NULL
968                    afterDelay:0.40];
969     }
970     else
971     {
972         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
973         {
974             // we just skipped 4 "continous" events, otherwise we are too fast
975             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
976             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
977             [self performSelector:@selector(resetBackwardSkip)
978                        withObject: NULL
979                        afterDelay:0.40];
980         }
981     }
982 }
983
984 - (IBAction)backward:(id)sender
985 {
986     [[VLCCoreInteraction sharedInstance] backwardShort];
987 }
988
989 - (void)resetNextButton
990 {
991     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
992         // seems like no further event occurred, so let's switch the playback item
993         [[VLCCoreInteraction sharedInstance] next];
994         just_triggered_next = NO;
995     }
996 }
997
998 - (void)resetForwardSkip
999 {
1000     // the user stopped skipping, so let's allow him to change the item
1001     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
1002         just_triggered_next = NO;
1003 }
1004
1005 - (IBAction)next:(id)sender
1006 {
1007     [[VLCCoreInteraction sharedInstance] next];
1008 }
1009
1010 - (IBAction)forward:(id)sender
1011 {
1012     [[VLCCoreInteraction sharedInstance] forwardShort];
1013 }
1014
1015 - (IBAction)fwd:(id)sender
1016 {
1017    if(!just_triggered_next)
1018     {
1019         just_triggered_next = YES;
1020         [self performSelector:@selector(resetNextButton)
1021                    withObject: NULL
1022                    afterDelay:0.40];
1023     }
1024     else
1025     {
1026         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.16 )
1027         {
1028             // we just skipped 4 "continous" events, otherwise we are too fast
1029             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
1030             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
1031             [self performSelector:@selector(resetForwardSkip)
1032                        withObject: NULL
1033                        afterDelay:0.40];
1034         }
1035     }
1036 }
1037
1038 - (IBAction)stop:(id)sender
1039 {
1040     [[VLCCoreInteraction sharedInstance] stop];
1041 }
1042
1043 - (void)resizePlaylistAfterCollapse
1044 {
1045     NSRect plrect;
1046     plrect = [o_playlist_table frame];
1047     plrect.size.height = i_lastSplitViewHeight - 20.0; // actual pl top bar height, which differs from its frame
1048     [[o_playlist_table animator] setFrame: plrect];
1049
1050     NSRect rightSplitRect;
1051     rightSplitRect = [o_right_split_view frame];
1052     plrect = [o_dropzone_box frame];
1053     plrect.origin.x = (rightSplitRect.size.width - plrect.size.width) / 2;
1054     plrect.origin.y = (rightSplitRect.size.height - plrect.size.height) / 2;
1055     [[o_dropzone_box animator] setFrame: plrect];
1056 }
1057
1058 - (void)makeSplitViewVisible
1059 {
1060     if( b_dark_interface )
1061         [self setContentMinSize: NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1062     else
1063         [self setContentMinSize: NSMakeSize( 604., 288. )];
1064
1065     NSRect old_frame = [self frame];
1066     float newHeight = [self minSize].height;
1067     if( old_frame.size.height < newHeight )
1068     {
1069         NSRect new_frame = old_frame;
1070         new_frame.origin.y = old_frame.origin.y + old_frame.size.height - newHeight;
1071         new_frame.size.height = newHeight;
1072
1073         [[self animator] setFrame: new_frame display: YES animate: YES];
1074     }
1075
1076     [o_video_view setHidden: YES];
1077     [o_split_view setHidden: NO];
1078     [self makeFirstResponder: nil];
1079
1080 }
1081
1082 - (void)makeSplitViewHidden
1083 {
1084     if( b_dark_interface )
1085         [self setContentMinSize: NSMakeSize( 604., f_min_video_height + [o_titlebar_view frame].size.height )];
1086     else
1087         [self setContentMinSize: NSMakeSize( 604., f_min_video_height )];
1088
1089     [o_split_view setHidden: YES];
1090     [o_video_view setHidden: NO];
1091
1092     if( [[o_video_view subviews] count] > 0 )
1093         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1094 }
1095
1096 - (IBAction)togglePlaylist:(id)sender
1097 {
1098     if (![self isVisible] && sender != nil)
1099     {
1100         [self makeKeyAndOrderFront: sender];
1101         return;
1102     }
1103
1104     BOOL b_activeVideo = [[VLCMain sharedInstance] activeVideoPlayback];
1105     BOOL b_restored = NO;
1106
1107     // TODO: implement toggle playlist in this situation (triggerd via menu item).
1108     // but for now we block this case, to avoid displaying only the half
1109     if( b_nativeFullscreenMode && b_fullscreen && b_activeVideo && sender != nil )
1110         return;
1111
1112     if (b_dropzone_active && ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0)
1113     {
1114         [self hideDropZone];
1115         return;
1116     }
1117
1118     if ( !(b_nativeFullscreenMode && b_fullscreen) && !b_splitview_removed && ( (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) != 0 && b_activeVideo)
1119                                   || (b_nonembedded && sender != nil)
1120                                   || (!b_activeVideo && sender != nil)
1121                                   || b_minimized_view ) )
1122     {
1123         [self hideSplitView];
1124     }
1125     else
1126     {
1127         if (b_splitview_removed)
1128         {
1129             if( !b_nonembedded || ( sender != nil && b_nonembedded))
1130                 [self showSplitView];
1131
1132             if (sender == nil)
1133                 b_minimized_view = YES;
1134             else
1135                 b_minimized_view = NO;
1136
1137             if (b_activeVideo)
1138                 b_restored = YES;
1139         }
1140
1141         if (!b_nonembedded)
1142         {
1143             if (([o_video_view isHidden] && b_activeVideo) || b_restored || (b_activeVideo && sender == nil) )
1144                 [self makeSplitViewHidden];
1145             else
1146                 [self makeSplitViewVisible];
1147         }
1148         else
1149         {
1150             [o_split_view setHidden: NO];
1151             [o_playlist_table setHidden: NO];
1152             [o_video_view setHidden: !b_activeVideo];
1153             if( b_activeVideo && [[o_video_view subviews] count] > 0 )
1154                 [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1155         }
1156     }
1157 }
1158
1159 - (void)setRepeatOne
1160 {
1161     [o_repeat_btn setImage: o_repeat_one_img];
1162     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];
1163 }
1164
1165 - (void)setRepeatAll
1166 {
1167     [o_repeat_btn setImage: o_repeat_all_img];
1168     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
1169 }
1170
1171 - (void)setRepeatOff
1172 {
1173     [o_repeat_btn setImage: o_repeat_img];
1174     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
1175 }
1176
1177 - (IBAction)repeat:(id)sender
1178 {
1179     vlc_value_t looping,repeating;
1180     intf_thread_t * p_intf = VLCIntf;
1181     playlist_t * p_playlist = pl_Get( p_intf );
1182
1183     var_Get( p_playlist, "repeat", &repeating );
1184     var_Get( p_playlist, "loop", &looping );
1185
1186     if( !repeating.b_bool && !looping.b_bool )
1187     {
1188         /* was: no repeating at all, switching to Repeat One */
1189         [[VLCCoreInteraction sharedInstance] repeatOne];
1190         [self setRepeatOne];
1191     }
1192     else if( repeating.b_bool && !looping.b_bool )
1193     {
1194         /* was: Repeat One, switching to Repeat All */
1195         [[VLCCoreInteraction sharedInstance] repeatAll];
1196         [self setRepeatAll];
1197     }
1198     else
1199     {
1200         /* was: Repeat All or bug in VLC, switching to Repeat Off */
1201         [[VLCCoreInteraction sharedInstance] repeatOff];
1202         [self setRepeatOff];
1203     }
1204 }
1205
1206 - (void)setShuffle
1207 {
1208     bool b_value;
1209     playlist_t *p_playlist = pl_Get( VLCIntf );
1210     b_value = var_GetBool( p_playlist, "random" );
1211
1212     if(b_value) {
1213         [o_shuffle_btn setImage: o_shuffle_on_img];
1214         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
1215     }
1216     else
1217     {
1218         [o_shuffle_btn setImage: o_shuffle_img];
1219         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
1220     }
1221 }
1222
1223 - (IBAction)shuffle:(id)sender
1224 {
1225     [[VLCCoreInteraction sharedInstance] shuffle];
1226     [self setShuffle];
1227 }
1228
1229 - (IBAction)timeSliderAction:(id)sender
1230 {
1231     float f_updated;
1232     input_thread_t * p_input;
1233
1234     switch( [[NSApp currentEvent] type] )
1235     {
1236         case NSLeftMouseUp:
1237         case NSLeftMouseDown:
1238         case NSLeftMouseDragged:
1239             f_updated = [sender floatValue];
1240             break;
1241
1242         default:
1243             return;
1244     }
1245     p_input = pl_CurrentInput( VLCIntf );
1246     if( p_input != NULL )
1247     {
1248         vlc_value_t pos;
1249         NSString * o_time;
1250
1251         pos.f_float = f_updated / 10000.;
1252         var_Set( p_input, "position", pos );
1253         [o_time_sld setFloatValue: f_updated];
1254
1255         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1256         [o_time_fld setStringValue: o_time];
1257         [o_fspanel setStreamPos: f_updated andTime: o_time];
1258         vlc_object_release( p_input );
1259     }
1260 }
1261
1262 - (IBAction)volumeAction:(id)sender
1263 {
1264     if (sender == o_volume_sld)
1265         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
1266     else if (sender == o_volume_down_btn)
1267         [[VLCCoreInteraction sharedInstance] toggleMute];
1268     else
1269         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
1270 }
1271
1272 - (IBAction)effects:(id)sender
1273 {
1274     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
1275 }
1276
1277 - (IBAction)fullscreen:(id)sender
1278 {
1279     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1280 }
1281
1282 - (IBAction)dropzoneButtonAction:(id)sender
1283 {
1284     [[[VLCMain sharedInstance] open] openFileGeneric];
1285 }
1286
1287 #pragma mark -
1288 #pragma mark overwritten default functionality
1289 - (BOOL)canBecomeKeyWindow
1290 {
1291     return YES;
1292 }
1293
1294 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
1295 {
1296     SEL s_menuAction = [menuItem action];
1297
1298     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
1299             return YES;
1300
1301     return [super validateMenuItem:menuItem];
1302 }
1303
1304 - (void)setTitle:(NSString *)title
1305 {
1306     if (b_dark_interface)
1307     {
1308         [o_titlebar_view setWindowTitle: title];
1309         if (b_video_deco)
1310             [o_detached_titlebar_view setWindowTitle: title];
1311     }
1312     if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
1313         [o_detached_video_window setTitle: title];
1314     [super setTitle: title];
1315 }
1316
1317 - (void)performClose:(id)sender
1318 {
1319     NSWindow *o_key_window = [NSApp keyWindow];
1320
1321     if (b_dark_interface || !b_video_deco)
1322     {
1323         [o_key_window orderOut: sender];
1324         if ( [[VLCMain sharedInstance] activeVideoPlayback] && ( !b_nonembedded || o_key_window != self ))
1325             [[VLCCoreInteraction sharedInstance] stop];
1326     }
1327     else
1328     {
1329         if( b_nonembedded && o_key_window != self )
1330             [o_detached_video_window performClose: sender];
1331         else
1332             [super performClose: sender];
1333     }
1334 }
1335
1336 - (void)performMiniaturize:(id)sender
1337 {
1338     if (b_dark_interface)
1339         [self miniaturize: sender];
1340     else
1341         [super performMiniaturize: sender];
1342 }
1343
1344 - (void)performZoom:(id)sender
1345 {
1346     if (b_dark_interface)
1347         [self customZoom: sender];
1348     else
1349         [super performZoom: sender];
1350 }
1351
1352 - (void)zoom:(id)sender
1353 {
1354     if (b_dark_interface)
1355         [self customZoom: sender];
1356     else
1357         [super zoom: sender];
1358 }
1359
1360 /**
1361  * Given a proposed frame rectangle, return a modified version
1362  * which will fit inside the screen.
1363  *
1364  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1365  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1366  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1367  *    Copyright (C) 1996 Free Software Foundation, Inc.
1368  */
1369 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
1370 {
1371     NSRect screenRect = [screen visibleFrame];
1372     float difference;
1373
1374     /* Move top edge of the window inside the screen */
1375     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
1376     if (difference > 0)
1377     {
1378         frameRect.origin.y -= difference;
1379     }
1380
1381     /* If the window is resizable, resize it (if needed) so that the
1382      bottom edge is on the screen or can be on the screen when the user moves
1383      the window */
1384     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
1385     if (_styleMask & NSResizableWindowMask)
1386     {
1387         float difference2;
1388
1389         difference2 = screenRect.origin.y - frameRect.origin.y;
1390         difference2 -= difference;
1391         // Take in account the space between the top of window and the top of the
1392         // screen which can be used to move the bottom of the window on the screen
1393         if (difference2 > 0)
1394         {
1395             frameRect.size.height -= difference2;
1396             frameRect.origin.y += difference2;
1397         }
1398
1399         /* Ensure that resizing doesn't makewindow smaller than minimum */
1400         difference2 = [self minSize].height - frameRect.size.height;
1401         if (difference2 > 0)
1402         {
1403             frameRect.size.height += difference2;
1404             frameRect.origin.y -= difference2;
1405         }
1406     }
1407
1408     return frameRect;
1409 }
1410
1411 #define DIST 3
1412
1413 /**
1414  Zooms the receiver.   This method calls the delegate method
1415  windowShouldZoom:toFrame: to determine if the window should
1416  be allowed to zoom to full screen.
1417  *
1418  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
1419  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
1420  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
1421  *    Copyright (C) 1996 Free Software Foundation, Inc.
1422  */
1423 - (void)customZoom:(id)sender
1424 {
1425     NSRect maxRect = [[self screen] visibleFrame];
1426     NSRect currentFrame = [self frame];
1427
1428     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
1429     {
1430         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
1431     }
1432
1433     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
1434
1435     // Compare the new frame with the current one
1436     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
1437         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
1438         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
1439         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
1440     {
1441         // Already in zoomed mode, reset user frame, if stored
1442         if ([self frameAutosaveName] != nil)
1443         {
1444             [self setFrame: previousSavedFrame display: YES animate: YES];
1445             [self saveFrameUsingName: [self frameAutosaveName]];
1446         }
1447         return;
1448     }
1449
1450     if ([self frameAutosaveName] != nil)
1451     {
1452         [self saveFrameUsingName: [self frameAutosaveName]];
1453         previousSavedFrame = [self frame];
1454     }
1455
1456     [self setFrame: maxRect display: YES animate: YES];
1457 }
1458
1459 - (void)windowResizedOrMoved:(NSNotification *)notification
1460 {
1461     [self saveFrameUsingName: [self frameAutosaveName]];
1462 }
1463
1464 - (void)applicationWillTerminate:(NSNotification *)notification
1465 {
1466     [self saveFrameUsingName: [self frameAutosaveName]];
1467 }
1468
1469 - (void)someWindowWillClose:(NSNotification *)notification
1470 {
1471     if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1472     {
1473         if ([[VLCMain sharedInstance] activeVideoPlayback])
1474             [[VLCCoreInteraction sharedInstance] stop];
1475     }
1476 }
1477
1478 - (void)someWindowWillMiniaturize:(NSNotification *)notification
1479 {
1480     if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
1481     {
1482         if([notification object] == o_detached_video_window || ([notification object] == self && !b_nonembedded))
1483         {
1484             if([[VLCMain sharedInstance] activeVideoPlayback])
1485                 [[VLCCoreInteraction sharedInstance] pause];
1486         }
1487     }
1488 }
1489
1490 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
1491 {
1492     id videoWindow = [o_video_view window];
1493     if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != videoWindow)
1494         return proposedFrameSize;
1495
1496     // needed when entering lion fullscreen mode
1497     if( b_fullscreen )
1498         return proposedFrameSize;
1499
1500     if( [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked] )
1501     {
1502         NSRect videoWindowFrame = [videoWindow frame];
1503         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
1504         NSRect contentRect = [videoWindow contentRectForFrameRect:videoWindowFrame];
1505         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
1506         float marginx = contentRect.size.width - viewRect.size.width;
1507         if( b_dark_interface && b_video_deco )
1508             marginy += [o_titlebar_view frame].size.height;
1509
1510         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
1511     }
1512
1513     return proposedFrameSize;
1514 }
1515
1516 #pragma mark -
1517 #pragma mark Update interface and respond to foreign events
1518 - (void)showDropZone
1519 {
1520     b_dropzone_active = YES;
1521     [o_right_split_view addSubview: o_dropzone_view positioned:NSWindowAbove relativeTo:o_playlist_table];
1522     [o_dropzone_view setFrame: [o_playlist_table frame]];
1523     [[o_playlist_table animator] setHidden:YES];
1524 }
1525
1526 - (void)hideDropZone
1527 {
1528     b_dropzone_active = NO;
1529     [o_dropzone_view removeFromSuperview];
1530     [[o_playlist_table animator] setHidden: NO];
1531 }
1532
1533 - (void)hideSplitView
1534 {
1535     NSRect winrect = [self frame];
1536     i_lastSplitViewHeight = [o_split_view frame].size.height;
1537     winrect.size.height = winrect.size.height - i_lastSplitViewHeight;
1538     winrect.origin.y = winrect.origin.y + i_lastSplitViewHeight;
1539     [self setFrame: winrect display: YES animate: YES];
1540     [self performSelector:@selector(hideDropZone) withObject:nil afterDelay:0.1];
1541     if (b_dark_interface)
1542     {
1543         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1544         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height + [o_titlebar_view frame].size.height )];
1545     }
1546     else
1547     {
1548         [self setContentMinSize: NSMakeSize( 604., [o_bottombar_view frame].size.height )];
1549         [self setContentMaxSize: NSMakeSize( FLT_MAX, [o_bottombar_view frame].size.height )];
1550     }
1551
1552     b_splitview_removed = YES;
1553 }
1554
1555 - (void)showSplitView
1556 {
1557     [self updateWindow];
1558     if (b_dark_interface)
1559         [self setContentMinSize:NSMakeSize( 604., 288. + [o_titlebar_view frame].size.height )];
1560     else
1561         [self setContentMinSize:NSMakeSize( 604., 288. )];
1562     [self setContentMaxSize: NSMakeSize( FLT_MAX, FLT_MAX )];
1563
1564     NSRect winrect;
1565     winrect = [self frame];
1566     winrect.size.height = winrect.size.height + i_lastSplitViewHeight;
1567     winrect.origin.y = winrect.origin.y - i_lastSplitViewHeight;
1568     [self setFrame: winrect display: YES animate: YES];
1569
1570     [self performSelector:@selector(resizePlaylistAfterCollapse) withObject: nil afterDelay:0.75];
1571
1572     b_splitview_removed = NO;
1573 }
1574
1575 - (void)updateTimeSlider
1576 {
1577     input_thread_t * p_input;
1578     p_input = pl_CurrentInput( VLCIntf );
1579     if( p_input )
1580     {
1581         NSString * o_time;
1582         vlc_value_t pos;
1583         float f_updated;
1584
1585         var_Get( p_input, "position", &pos );
1586         f_updated = 10000. * pos.f_float;
1587         [o_time_sld setFloatValue: f_updated];
1588
1589         o_time = [[VLCStringUtility sharedInstance] getCurrentTimeAsString: p_input negative:[o_time_fld timeRemaining]];
1590
1591         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1592         if (dur == -1) {
1593             [o_time_sld setEnabled: NO];
1594             [o_time_sld setHidden: YES];
1595             [o_time_sld_fancygradient_view setHidden: YES];
1596         } else {
1597             [o_time_sld setEnabled: YES];
1598             [o_time_sld setHidden: NO];
1599             [o_time_sld_fancygradient_view setHidden: NO];
1600         }
1601
1602         [o_time_fld setStringValue: o_time];
1603         [o_time_fld setNeedsDisplay:YES];
1604         [o_fspanel setStreamPos: f_updated andTime: o_time];
1605         vlc_object_release( p_input );
1606     }
1607     else
1608     {
1609         [o_time_sld setFloatValue: 0.0];
1610         [o_time_fld setStringValue: @"00:00"];
1611         [o_time_sld setEnabled: NO];
1612         [o_time_sld setHidden: YES];
1613         [o_time_sld_fancygradient_view setHidden: YES];
1614         if (b_video_deco)
1615             [o_detached_time_sld_fancygradient_view setHidden: YES];
1616     }
1617
1618     if (b_video_deco)
1619     {
1620         [o_detached_time_sld setFloatValue: [o_time_sld floatValue]];
1621         [o_detached_time_sld setEnabled: [o_time_sld isEnabled]];
1622         [o_detached_time_fld setStringValue: [o_time_fld stringValue]];
1623         [o_detached_time_sld setHidden: [o_time_sld isHidden]];
1624     }
1625 }
1626
1627 - (void)updateVolumeSlider
1628 {
1629     int i_volume = [[VLCCoreInteraction sharedInstance] volume];
1630     BOOL b_muted = [[VLCCoreInteraction sharedInstance] mute];
1631
1632     if( !b_muted )
1633     {
1634         [o_volume_sld setIntValue: i_volume];
1635         [o_fspanel setVolumeLevel: i_volume];
1636     }
1637     else
1638         [o_volume_sld setIntValue: 0];
1639
1640     [o_volume_sld setEnabled: !b_muted];
1641     [o_volume_up_btn setEnabled: !b_muted];
1642 }
1643
1644 - (void)updateName
1645 {
1646     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1647     input_thread_t * p_input;
1648     p_input = pl_CurrentInput( VLCIntf );
1649     if( p_input )
1650     {
1651         NSString *aString;
1652         char *format = var_InheritString( VLCIntf, "input-title-format" );
1653         char *formated = str_format_meta( pl_Get( VLCIntf ), format );
1654         free( format );
1655         aString = [NSString stringWithUTF8String:formated];
1656         free( formated );
1657
1658         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1659
1660         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1661         if ([o_url isFileURL])
1662         {
1663             [self setRepresentedURL: o_url];
1664             [o_detached_video_window setRepresentedURL: o_url];
1665         } else {
1666             [self setRepresentedURL: nil];
1667             [o_detached_video_window setRepresentedURL: nil];
1668         }
1669         free( uri );
1670
1671         if ([aString isEqualToString:@""])
1672         {
1673             if ([o_url isFileURL])
1674                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1675             else
1676                 aString = [o_url absoluteString];
1677         }
1678
1679         [self setTitle: aString];
1680         [o_fspanel setStreamTitle: aString];
1681         vlc_object_release( p_input );
1682     }
1683     else
1684     {
1685         [self setTitle: _NS("VLC media player")];
1686         [self setRepresentedURL: nil];
1687     }
1688
1689     [o_pool release];
1690 }
1691
1692 - (void)updateWindow
1693 {
1694     bool b_input = false;
1695     bool b_plmul = false;
1696     bool b_control = false;
1697     bool b_seekable = false;
1698     bool b_chapters = false;
1699
1700     playlist_t * p_playlist = pl_Get( VLCIntf );
1701
1702     PL_LOCK;
1703     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1704     PL_UNLOCK;
1705
1706     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1707
1708     bool b_buffering = NO;
1709
1710     if( ( b_input = ( p_input != NULL ) ) )
1711     {
1712         /* seekable streams */
1713         cachedInputState = input_GetState( p_input );
1714         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1715             b_buffering = YES;
1716
1717         /* seekable streams */
1718         b_seekable = var_GetBool( p_input, "can-seek" );
1719
1720         /* check whether slow/fast motion is possible */
1721         b_control = var_GetBool( p_input, "can-rate" );
1722
1723         /* chapters & titles */
1724         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1725
1726         vlc_object_release( p_input );
1727     }
1728
1729     if( b_buffering )
1730     {
1731         [o_progress_bar startAnimation:self];
1732         [o_progress_bar setIndeterminate:YES];
1733         [o_progress_bar setHidden:NO];
1734     } else {
1735         [o_progress_bar stopAnimation:self];
1736         [o_progress_bar setHidden:YES];
1737     }
1738
1739     [o_stop_btn setEnabled: b_input];
1740     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1741     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1742     if (b_show_jump_buttons)
1743     {
1744         [o_prev_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1745         [o_next_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1746     }
1747     if (b_video_deco)
1748     {
1749         [o_detached_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1750         [o_detached_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1751     }
1752     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1753
1754     [o_time_sld setEnabled: b_seekable];
1755     [self updateTimeSlider];
1756     [o_fspanel setSeekable: b_seekable];
1757
1758     PL_LOCK;
1759     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1760         [self hideDropZone];
1761     else
1762         [self showDropZone];
1763     PL_UNLOCK;
1764     [o_sidebar_view setNeedsDisplay:YES];
1765 }
1766
1767 - (void)setPause
1768 {
1769     [o_play_btn setImage: o_pause_img];
1770     [o_play_btn setAlternateImage: o_pause_pressed_img];
1771     [o_play_btn setToolTip: _NS("Pause")];
1772     if (b_video_deco)
1773     {
1774         [o_detached_play_btn setImage: o_pause_img];
1775         [o_detached_play_btn setAlternateImage: o_pause_pressed_img];
1776         [o_detached_play_btn setToolTip: _NS("Pause")];
1777     }
1778     [o_fspanel setPause];
1779 }
1780
1781 - (void)setPlay
1782 {
1783     [o_play_btn setImage: o_play_img];
1784     [o_play_btn setAlternateImage: o_play_pressed_img];
1785     [o_play_btn setToolTip: _NS("Play")];
1786     if (b_video_deco)
1787     {
1788         [o_detached_play_btn setImage: o_play_img];
1789         [o_detached_play_btn setAlternateImage: o_play_pressed_img];
1790         [o_detached_play_btn setToolTip: _NS("Play")];
1791     }
1792     [o_fspanel setPlay];
1793 }
1794
1795 - (void)drawFancyGradientEffectForTimeSlider
1796 {
1797     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1798     CGFloat f_value = [o_time_sld knobPosition];
1799     if (f_value > 7.5)
1800     {
1801         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1802         if (f_value != oldFrame.size.width)
1803         {
1804             if ([o_time_sld_fancygradient_view isHidden])
1805                 [o_time_sld_fancygradient_view setHidden: NO];
1806             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1807         }
1808
1809         if (b_nonembedded && b_video_deco)
1810         {
1811             f_value = [o_detached_time_sld knobPosition];
1812             oldFrame = [o_detached_time_sld_fancygradient_view frame];
1813             if (f_value != oldFrame.size.width)
1814             {
1815                 if ([o_detached_time_sld_fancygradient_view isHidden])
1816                     [o_detached_time_sld_fancygradient_view setHidden: NO];
1817                 [o_detached_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1818             }
1819         }
1820     }
1821     else
1822     {
1823         NSRect frame;
1824         frame = [o_time_sld_fancygradient_view frame];
1825         if (frame.size.width > 0)
1826         {
1827             frame.size.width = 0;
1828             [o_time_sld_fancygradient_view setFrame: frame];
1829
1830             if (b_video_deco)
1831             {
1832                 frame = [o_detached_time_sld_fancygradient_view frame];
1833                 frame.size.width = 0;
1834                 [o_detached_time_sld_fancygradient_view setFrame: frame];
1835             }
1836         }
1837         [o_time_sld_fancygradient_view setHidden: YES];
1838         if (b_video_deco)
1839             [o_detached_time_sld_fancygradient_view setHidden: YES];
1840     }
1841     [o_pool release];
1842 }
1843
1844 #pragma mark -
1845 #pragma mark Video Output handling
1846 - (id)videoView
1847 {
1848     return o_video_view;
1849 }
1850
1851 - (void)setupVideoView
1852 {
1853     // TODO: make lion fullscreen compatible with macosx-background and !embedded-video
1854     if( var_InheritBool( VLCIntf, "macosx-background" ) && !b_nativeFullscreenMode )
1855     {
1856         msg_Dbg( VLCIntf, "Creating background window" );
1857         NSScreen *screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
1858         if( !screen )
1859             screen = [self screen];
1860         NSRect screen_rect = [screen frame];
1861
1862         if( o_extra_video_window )
1863             [o_extra_video_window release];
1864
1865         o_extra_video_window = [[VLCWindow alloc] initWithContentRect:screen_rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
1866         [o_extra_video_window setLevel: CGWindowLevelForKey(kCGDesktopWindowLevelKey) + 1];
1867         [o_extra_video_window setBackgroundColor: [NSColor blackColor]];
1868         [o_extra_video_window setCanBecomeKeyWindow: NO];
1869         [o_extra_video_window setCanBecomeMainWindow: NO];
1870         [o_extra_video_window useOptimizedDrawing: YES];
1871         [o_extra_video_window setMovableByWindowBackground: NO];
1872
1873         [o_video_view retain];
1874         if ([o_video_view superview] != NULL)
1875             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1876         screen_rect.origin.x = screen_rect.origin.y = 0;
1877         [o_video_view setFrame: screen_rect];
1878         [[o_extra_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1879         [o_video_view release];
1880
1881         [o_extra_video_window orderBack:nil];
1882
1883         b_nonembedded = YES;
1884     }
1885     else
1886     {
1887         if ((var_InheritBool( VLCIntf, "embedded-video" ) || b_nativeFullscreenMode) && b_video_deco)
1888         {
1889             if ([o_video_view window] != self)
1890             {
1891                 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1892                 [o_video_view setFrame: [o_split_view frame]];
1893                 [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1894             }
1895             b_nonembedded = NO;
1896         }
1897         else
1898         {
1899             if ([o_video_view superview] != NULL)
1900                 [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1901
1902             NSRect videoFrame;
1903             videoFrame.size = [[o_detached_video_window contentView] frame].size;
1904             if (b_video_deco)
1905             {
1906                 videoFrame.size.height -= [o_detached_bottombar_view frame].size.height;
1907                 if( b_dark_interface )
1908                     videoFrame.size.height -= [o_detached_titlebar_view frame].size.height;
1909
1910                 videoFrame.origin.x = .0;
1911                 videoFrame.origin.y = [o_detached_bottombar_view frame].size.height;
1912             }
1913             else
1914             {
1915                 videoFrame.origin.y = .0;
1916                 videoFrame.origin.x = .0;
1917             }
1918
1919             [o_video_view setFrame: videoFrame];
1920             [[o_detached_video_window contentView] addSubview: o_video_view positioned:NSWindowAbove relativeTo:nil];
1921             [o_detached_video_window setLevel:NSNormalWindowLevel];
1922             [o_detached_video_window useOptimizedDrawing: YES];
1923             b_nonembedded = YES;
1924         }
1925
1926         [[o_video_view window] makeKeyAndOrderFront: self];
1927
1928         vout_thread_t *p_vout = getVout();
1929         if (p_vout)
1930         {
1931             if( var_GetBool( p_vout, "video-on-top" ) )
1932                 [[o_video_view window] setLevel: NSStatusWindowLevel];
1933             else
1934                 [[o_video_view window] setLevel: NSNormalWindowLevel];
1935             vlc_object_release( p_vout );
1936         }
1937     }
1938
1939     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
1940 }
1941
1942 - (void)setVideoplayEnabled
1943 {
1944     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1945
1946     if( b_videoPlayback )
1947     {
1948         // look for 'start at fullscreen'
1949         [[VLCMain sharedInstance] fullscreenChanged];
1950     }
1951     else
1952     {
1953         [self makeFirstResponder: nil];
1954         [o_detached_video_window orderOut: nil];
1955         if( o_extra_video_window )
1956             [o_extra_video_window orderOut: nil];
1957
1958         if( [self level] != NSNormalWindowLevel )
1959             [self setLevel: NSNormalWindowLevel];
1960         if( [o_detached_video_window level] != NSNormalWindowLevel )
1961             [o_detached_video_window setLevel: NSNormalWindowLevel];
1962
1963         // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
1964         [self setAlphaValue:1.0];
1965     }
1966
1967     if( b_nativeFullscreenMode )
1968     {
1969         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1970             [o_bottombar_view setHidden: b_videoPlayback];
1971         else
1972             [o_bottombar_view setHidden: NO];
1973         if( b_videoPlayback && b_fullscreen )
1974             [o_fspanel setActive: nil];
1975         if( !b_videoPlayback )
1976             [o_fspanel setNonActive: nil];
1977     }
1978
1979     if (!b_videoPlayback && b_fullscreen)
1980     {
1981         if (!b_nativeFullscreenMode)
1982             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1983     }
1984 }
1985
1986 - (void)resizeWindow
1987 {
1988     if( b_fullscreen || ( b_nativeFullscreenMode && [NSApp presentationOptions] & NSApplicationPresentationFullScreen ) )
1989         return;
1990
1991     id o_videoWindow = b_nonembedded ? o_detached_video_window : self;
1992     NSSize windowMinSize = [o_videoWindow minSize];
1993     NSRect screenFrame = [[o_videoWindow screen] visibleFrame];
1994
1995     NSPoint topleftbase = NSMakePoint( 0, [o_videoWindow frame].size.height );
1996     NSPoint topleftscreen = [o_videoWindow convertBaseToScreen: topleftbase];
1997
1998     unsigned int i_width = nativeVideoSize.width;
1999     unsigned int i_height = nativeVideoSize.height;
2000     if (i_width < windowMinSize.width)
2001         i_width = windowMinSize.width;
2002     if (i_height < f_min_video_height)
2003         i_height = f_min_video_height;
2004
2005     /* Calculate the window's new size */
2006     NSRect new_frame;
2007     new_frame.size.width = [o_videoWindow frame].size.width - [o_video_view frame].size.width + i_width;
2008     new_frame.size.height = [o_videoWindow frame].size.height - [o_video_view frame].size.height + i_height;
2009     new_frame.origin.x = topleftscreen.x;
2010     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
2011
2012     /* make sure the window doesn't exceed the screen size the window is on */
2013     if( new_frame.size.width > screenFrame.size.width )
2014     {
2015         new_frame.size.width = screenFrame.size.width;
2016         new_frame.origin.x = screenFrame.origin.x;
2017     }
2018     if( new_frame.size.height > screenFrame.size.height )
2019     {
2020         new_frame.size.height = screenFrame.size.height;
2021         new_frame.origin.y = screenFrame.origin.y;
2022     }
2023     if( new_frame.origin.y < screenFrame.origin.y )
2024         new_frame.origin.y = screenFrame.origin.y;
2025
2026     CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
2027     CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
2028     if( right_window_point > right_screen_point )
2029         new_frame.origin.x -= ( right_window_point - right_screen_point );
2030
2031     [[o_videoWindow animator] setFrame:new_frame display:YES];
2032 }
2033
2034 - (void)setNativeVideoSize:(NSSize)size
2035 {
2036     nativeVideoSize = size;
2037
2038     if( var_InheritBool( VLCIntf, "macosx-video-autoresize" ) && !b_fullscreen && !var_InheritBool( VLCIntf, "macosx-background" ) )
2039         [self performSelectorOnMainThread:@selector(resizeWindow) withObject:nil waitUntilDone:NO];
2040 }
2041
2042 //  Called automatically if window's acceptsMouseMovedEvents property is true
2043 - (void)mouseMoved:(NSEvent *)theEvent
2044 {
2045     if (b_fullscreen)
2046         [self recreateHideMouseTimer];
2047
2048     [super mouseMoved: theEvent];
2049 }
2050
2051 - (void)recreateHideMouseTimer
2052 {
2053     if (t_hide_mouse_timer != nil) {
2054         [t_hide_mouse_timer invalidate];
2055         [t_hide_mouse_timer release];
2056     }
2057
2058     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
2059                                                           target:self
2060                                                         selector:@selector(hideMouseCursor:)
2061                                                         userInfo:nil
2062                                                          repeats:NO];
2063     [t_hide_mouse_timer retain];
2064 }
2065
2066 //  NSTimer selectors require this function signature as per Apple's docs
2067 - (void)hideMouseCursor:(NSTimer *)timer
2068 {
2069     [NSCursor setHiddenUntilMouseMoves: YES];
2070 }
2071
2072 #pragma mark -
2073 #pragma mark Fullscreen support
2074 - (void)showFullscreenController
2075 {
2076      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
2077         [o_fspanel fadeIn];
2078 }
2079
2080 - (BOOL)fullscreen
2081 {
2082     return b_fullscreen;
2083 }
2084
2085 - (void)lockFullscreenAnimation
2086 {
2087     [o_animation_lock lock];
2088 }
2089
2090 - (void)unlockFullscreenAnimation
2091 {
2092     [o_animation_lock unlock];
2093 }
2094
2095 - (void)enterFullscreen
2096 {
2097     NSMutableDictionary *dict1, *dict2;
2098     NSScreen *screen;
2099     NSRect screen_rect;
2100     NSRect rect;
2101     BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2102     o_current_video_window = [o_video_view window];
2103
2104     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger( VLCIntf, "macosx-vdev" )];
2105     [self lockFullscreenAnimation];
2106
2107     if (!screen)
2108     {
2109         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
2110         screen = [o_current_video_window screen];
2111     }
2112     if (!screen)
2113     {
2114         msg_Dbg( VLCIntf, "Using deepest screen" );
2115         screen = [NSScreen deepestScreen];
2116     }
2117
2118     screen_rect = [screen frame];
2119
2120     [o_fullscreen_btn setState: YES];
2121     if (b_video_deco)
2122         [o_detached_fullscreen_btn setState: YES];
2123
2124     [self recreateHideMouseTimer];
2125
2126     if( blackout_other_displays )
2127         [screen blackoutOtherScreens];
2128
2129     /* Make sure we don't see the window flashes in float-on-top mode */
2130     i_originalLevel = [o_current_video_window level];
2131     [o_current_video_window setLevel:NSNormalWindowLevel];
2132
2133     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
2134     if (!o_fullscreen_window)
2135     {
2136         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
2137
2138         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
2139         rect.origin.x += [o_current_video_window frame].origin.x;
2140         rect.origin.y += [o_current_video_window frame].origin.y;
2141         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
2142         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
2143         [o_fullscreen_window setCanBecomeKeyWindow: YES];
2144         [o_fullscreen_window setCanBecomeMainWindow: YES];
2145
2146         if (![o_current_video_window isVisible] || [o_current_video_window alphaValue] == 0.0)
2147         {
2148             /* We don't animate if we are not visible, instead we
2149              * simply fade the display */
2150             CGDisplayFadeReservationToken token;
2151
2152             if( blackout_other_displays )
2153             {
2154                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2155                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2156             }
2157
2158             if ([screen mainScreen])
2159                 [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2160
2161             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2162             [o_temp_view setFrame:[o_video_view frame]];
2163             [o_fullscreen_window setContentView:o_video_view];
2164
2165             [o_fullscreen_window makeKeyAndOrderFront:self];
2166             [o_fullscreen_window orderFront:self animate:YES];
2167
2168             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
2169             [o_fullscreen_window setLevel:NSNormalWindowLevel];
2170
2171             if( blackout_other_displays )
2172             {
2173                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2174                 CGReleaseDisplayFadeReservation( token );
2175             }
2176
2177             /* Will release the lock */
2178             [self hasBecomeFullscreen];
2179
2180             return;
2181         }
2182
2183         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2184         NSDisableScreenUpdates();
2185         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
2186         [o_temp_view setFrame:[o_video_view frame]];
2187         [o_fullscreen_window setContentView:o_video_view];
2188         [o_fullscreen_window makeKeyAndOrderFront:self];
2189         NSEnableScreenUpdates();
2190     }
2191
2192     /* We are in fullscreen (and no animation is running) */
2193     if (b_fullscreen)
2194     {
2195         /* Make sure we are hidden */
2196         [o_current_video_window orderOut: self];
2197
2198         [self unlockFullscreenAnimation];
2199         return;
2200     }
2201
2202     if (o_fullscreen_anim1)
2203     {
2204         [o_fullscreen_anim1 stopAnimation];
2205         [o_fullscreen_anim1 release];
2206     }
2207     if (o_fullscreen_anim2)
2208     {
2209         [o_fullscreen_anim2 stopAnimation];
2210         [o_fullscreen_anim2 release];
2211     }
2212
2213     if ([screen mainScreen])
2214         [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2215
2216     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
2217     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
2218
2219     [dict1 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2220     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
2221
2222     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2223     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2224     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
2225
2226     /* Strategy with NSAnimation allocation:
2227      - Keep at most 2 animation at a time
2228      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
2229      */
2230     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
2231     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
2232
2233     [dict1 release];
2234     [dict2 release];
2235
2236     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2237     [o_fullscreen_anim1 setDuration: 0.3];
2238     [o_fullscreen_anim1 setFrameRate: 30];
2239     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2240     [o_fullscreen_anim2 setDuration: 0.2];
2241     [o_fullscreen_anim2 setFrameRate: 30];
2242
2243     [o_fullscreen_anim2 setDelegate: self];
2244     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2245
2246     [o_fullscreen_anim1 startAnimation];
2247     /* fullscreenAnimation will be unlocked when animation ends */
2248 }
2249
2250 - (void)hasBecomeFullscreen
2251 {
2252     if( [[o_video_view subviews] count] > 0 )
2253         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2254
2255     [o_fullscreen_window makeKeyWindow];
2256     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
2257
2258     /* tell the fspanel to move itself to front next time it's triggered */
2259     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
2260     [o_fspanel setActive: nil];
2261
2262     if( [o_current_video_window isVisible] )
2263         [o_current_video_window orderOut: self];
2264
2265     b_fullscreen = YES;
2266     [self unlockFullscreenAnimation];
2267 }
2268
2269 - (void)leaveFullscreen
2270 {
2271     [self leaveFullscreenAndFadeOut: NO];
2272 }
2273
2274 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
2275 {
2276     NSMutableDictionary *dict1, *dict2;
2277     NSRect frame;
2278     BOOL blackout_other_displays = var_InheritBool( VLCIntf, "macosx-black" );
2279
2280     if( !o_current_video_window )
2281         return;
2282
2283     [self lockFullscreenAnimation];
2284
2285     [o_fullscreen_btn setState: NO];
2286     if (b_video_deco)
2287         [o_detached_fullscreen_btn setState: NO];
2288
2289     /* We always try to do so */
2290     [NSScreen unblackoutScreens];
2291
2292     vout_thread_t *p_vout = getVout();
2293     if (p_vout)
2294     {
2295         if( var_GetBool( p_vout, "video-on-top" ) )
2296             [[o_video_view window] setLevel: NSStatusWindowLevel];
2297         else
2298             [[o_video_view window] setLevel: NSNormalWindowLevel];
2299         vlc_object_release( p_vout );
2300     }
2301     [[o_video_view window] makeKeyAndOrderFront: nil];
2302
2303     /* Don't do anything if o_fullscreen_window is already closed */
2304     if (!o_fullscreen_window)
2305     {
2306         [self unlockFullscreenAnimation];
2307         return;
2308     }
2309
2310     if (fadeout)
2311     {
2312         /* We don't animate if we are not visible, instead we
2313          * simply fade the display */
2314         CGDisplayFadeReservationToken token;
2315
2316         if( blackout_other_displays )
2317         {
2318             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
2319             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
2320         }
2321
2322         [o_fspanel setNonActive: nil];
2323         [NSApp setPresentationOptions: NSApplicationPresentationDefault];
2324
2325         /* Will release the lock */
2326         [self hasEndedFullscreen];
2327
2328         /* Our window is hidden, and might be faded. We need to workaround that, so note it
2329          * here */
2330         b_window_is_invisible = YES;
2331
2332         if( blackout_other_displays )
2333         {
2334             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
2335             CGReleaseDisplayFadeReservation( token );
2336         }
2337
2338         return;
2339     }
2340
2341     [o_current_video_window setAlphaValue: 0.0];
2342     [o_current_video_window orderFront: self];
2343     [[o_video_view window] orderFront: self];
2344
2345     [o_fspanel setNonActive: nil];
2346     [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
2347
2348     if (o_fullscreen_anim1)
2349     {
2350         [o_fullscreen_anim1 stopAnimation];
2351         [o_fullscreen_anim1 release];
2352     }
2353     if (o_fullscreen_anim2)
2354     {
2355         [o_fullscreen_anim2 stopAnimation];
2356         [o_fullscreen_anim2 release];
2357     }
2358
2359     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
2360     frame.origin.x += [o_current_video_window frame].origin.x;
2361     frame.origin.y += [o_current_video_window frame].origin.y;
2362
2363     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
2364     [dict2 setObject:o_current_video_window forKey:NSViewAnimationTargetKey];
2365     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2366
2367     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
2368     [dict2 release];
2369
2370     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
2371     [o_fullscreen_anim2 setDuration: 0.3];
2372     [o_fullscreen_anim2 setFrameRate: 30];
2373
2374     [o_fullscreen_anim2 setDelegate: self];
2375
2376     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
2377
2378     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
2379     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
2380     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
2381
2382     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
2383     [dict1 release];
2384
2385     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
2386     [o_fullscreen_anim1 setDuration: 0.2];
2387     [o_fullscreen_anim1 setFrameRate: 30];
2388     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
2389
2390     /* Make sure o_fullscreen_window is the frontmost window */
2391     [o_fullscreen_window orderFront: self];
2392
2393     [o_fullscreen_anim1 startAnimation];
2394     /* fullscreenAnimation will be unlocked when animation ends */
2395 }
2396
2397 - (void)hasEndedFullscreen
2398 {
2399     b_fullscreen = NO;
2400
2401     /* This function is private and should be only triggered at the end of the fullscreen change animation */
2402     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
2403     NSDisableScreenUpdates();
2404     [o_video_view retain];
2405     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
2406     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
2407     [o_video_view release];
2408     [o_video_view setFrame:[o_temp_view frame]];
2409     if( [[o_video_view subviews] count] > 0 )
2410         [[o_video_view window] makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
2411     if( !b_nonembedded )
2412             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
2413     else
2414         [[o_video_view window] makeKeyAndOrderFront: self];
2415     [o_fullscreen_window orderOut: self];
2416     NSEnableScreenUpdates();
2417
2418     [o_fullscreen_window release];
2419     o_fullscreen_window = nil;
2420     [[o_video_view window] setLevel:i_originalLevel];
2421     [[o_video_view window] setAlphaValue: config_GetFloat( VLCIntf, "macosx-opaqueness" )];
2422
2423     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
2424     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
2425         [o_current_video_window orderOut: self];
2426
2427     o_current_video_window = nil;
2428     [self unlockFullscreenAnimation];
2429 }
2430
2431 - (void)animationDidEnd:(NSAnimation*)animation
2432 {
2433     NSArray *viewAnimations;
2434     if( o_makekey_anim == animation )
2435     {
2436         [o_makekey_anim release];
2437         return;
2438     }
2439     if ([animation currentValue] < 1.0)
2440         return;
2441
2442     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
2443     viewAnimations = [o_fullscreen_anim2 viewAnimations];
2444     if ([viewAnimations count] >=1 &&
2445         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
2446     {
2447         /* Fullscreen ended */
2448         [self hasEndedFullscreen];
2449     }
2450     else
2451     {
2452         /* Fullscreen started */
2453         [self hasBecomeFullscreen];
2454     }
2455 }
2456
2457 - (void)makeKeyAndOrderFront: (id)sender
2458 {
2459     /* Hack
2460      * when we exit fullscreen and fade out, we may endup in
2461      * having a window that is faded. We can't have it fade in unless we
2462      * animate again. */
2463
2464     if(!b_window_is_invisible)
2465     {
2466         /* Make sure we don't do it too much */
2467         [super makeKeyAndOrderFront: sender];
2468         return;
2469     }
2470
2471     [super setAlphaValue:0.0f];
2472     [super makeKeyAndOrderFront: sender];
2473
2474     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
2475     [dict setObject:self forKey:NSViewAnimationTargetKey];
2476     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
2477
2478     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
2479     [dict release];
2480
2481     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
2482     [o_makekey_anim setDuration: 0.1];
2483     [o_makekey_anim setFrameRate: 30];
2484     [o_makekey_anim setDelegate: self];
2485
2486     [o_makekey_anim startAnimation];
2487     b_window_is_invisible = NO;
2488
2489     /* fullscreenAnimation will be unlocked when animation ends */
2490 }
2491
2492 #pragma mark -
2493 #pragma mark Lion native fullscreen handling
2494 - (void)windowWillEnterFullScreen:(NSNotification *)notification
2495 {
2496     // workaround, see #6668
2497     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
2498
2499     var_SetBool( pl_Get( VLCIntf ), "fullscreen", true );
2500
2501     vout_thread_t *p_vout = getVout();
2502     if( p_vout )
2503     {
2504         var_SetBool( p_vout, "fullscreen", true );
2505         vlc_object_release( p_vout );
2506     }
2507
2508     [o_video_view setFrame: [[self contentView] frame]];
2509     b_fullscreen = YES;
2510
2511     [self recreateHideMouseTimer];
2512     i_originalLevel = [self level];
2513     [self setLevel:NSNormalWindowLevel];
2514
2515     if (b_dark_interface)
2516     {
2517         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
2518
2519         NSRect winrect;
2520         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2521         winrect = [self frame];
2522
2523         winrect.size.height = winrect.size.height - f_titleBarHeight;
2524         [self setFrame: winrect display:NO animate:NO];
2525         winrect = [o_split_view frame];
2526         winrect.size.height = winrect.size.height + f_titleBarHeight;
2527         [o_split_view setFrame: winrect];
2528     }
2529
2530     if ([[VLCMain sharedInstance] activeVideoPlayback])
2531         [o_bottombar_view setHidden: YES];
2532
2533     [self setMovableByWindowBackground: NO];
2534 }
2535
2536 - (void)windowDidEnterFullScreen:(NSNotification *)notification
2537 {
2538     // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
2539     // But this creates some problems when leaving fs over remote intfs, so activate app here.
2540     [NSApp activateIgnoringOtherApps:YES];
2541
2542     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
2543     [o_fspanel setActive: nil];
2544 }
2545
2546 - (void)windowWillExitFullScreen:(NSNotification *)notification
2547 {
2548
2549     var_SetBool( pl_Get( VLCIntf ), "fullscreen", false );
2550
2551     vout_thread_t *p_vout = getVout();
2552     if( p_vout )
2553     {
2554         var_SetBool( p_vout, "fullscreen", false );
2555         vlc_object_release( p_vout );
2556     }
2557
2558     [o_video_view setFrame: [o_split_view frame]];
2559     [NSCursor setHiddenUntilMouseMoves: NO];
2560     [o_fspanel setNonActive: nil];
2561     [self setLevel:i_originalLevel];
2562     b_fullscreen = NO;
2563
2564     if (b_dark_interface)
2565     {
2566         NSRect winrect;
2567         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
2568         winrect = [self frame];
2569
2570         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
2571                                               winrect.size.width, f_titleBarHeight )];
2572         [[self contentView] addSubview: o_titlebar_view];
2573
2574         winrect.size.height = winrect.size.height + f_titleBarHeight;
2575         [self setFrame: winrect display:NO animate:NO];
2576         winrect = [o_split_view frame];
2577         winrect.size.height = winrect.size.height - f_titleBarHeight;
2578         [o_split_view setFrame: winrect];
2579         [o_video_view setFrame: winrect];
2580     }
2581
2582     if ([[VLCMain sharedInstance] activeVideoPlayback])
2583         [o_bottombar_view setHidden: NO];
2584
2585     [self setMovableByWindowBackground: YES];
2586 }
2587
2588 #pragma mark -
2589 #pragma mark split view delegate
2590 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
2591 {
2592     if (dividerIndex == 0)
2593         return 300.;
2594     else
2595         return proposedMax;
2596 }
2597
2598 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
2599 {
2600     if (dividerIndex == 0)
2601         return 100.;
2602     else
2603         return proposedMin;
2604 }
2605
2606 - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview
2607 {
2608     return ([subview isEqual:o_left_split_view]);
2609 }
2610
2611 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview
2612 {
2613     if ([subview isEqual:o_left_split_view])
2614         return NO;
2615     return YES;
2616 }
2617
2618 #pragma mark -
2619 #pragma mark Side Bar Data handling
2620 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2621 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
2622 {
2623     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2624     if(item==nil)
2625         return [o_sidebaritems count];
2626     else
2627         return [[item children] count];
2628 }
2629
2630
2631 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
2632 {
2633     //Works the same way as the NSOutlineView data source: `nil` means a parent item
2634     if(item==nil)
2635         return [o_sidebaritems objectAtIndex:index];
2636     else
2637         return [[item children] objectAtIndex:index];
2638 }
2639
2640
2641 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
2642 {
2643     return [item title];
2644 }
2645
2646 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
2647 {
2648     [item setTitle:object];
2649 }
2650
2651 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
2652 {
2653     return [item hasChildren];
2654 }
2655
2656
2657 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
2658 {
2659     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
2660         return YES;
2661
2662     return [item hasBadge];
2663 }
2664
2665
2666 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
2667 {
2668     playlist_t * p_playlist = pl_Get( VLCIntf );
2669     NSInteger i_playlist_size;
2670
2671     if ([[item identifier] isEqualToString: @"playlist"])
2672     {
2673         PL_LOCK;
2674         i_playlist_size = p_playlist->p_local_category->i_children;
2675         PL_UNLOCK;
2676
2677         return i_playlist_size;
2678     }
2679     if ([[item identifier] isEqualToString: @"medialibrary"])
2680     {
2681         PL_LOCK;
2682         i_playlist_size = p_playlist->p_ml_category->i_children;
2683         PL_UNLOCK;
2684
2685         return i_playlist_size;
2686     }
2687
2688     return [item badgeValue];
2689 }
2690
2691
2692 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
2693 {
2694     return [item hasIcon];
2695 }
2696
2697
2698 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
2699 {
2700     return [item icon];
2701 }
2702
2703 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
2704 {
2705     if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask))
2706     {
2707         if (item != nil)
2708         {
2709             NSMenu * m;
2710             if ([item sdtype] > 0)
2711             {
2712                 m = [[NSMenu alloc] init];
2713                 playlist_t * p_playlist = pl_Get( VLCIntf );
2714                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2715                 if (!sd_loaded)
2716                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2717                 else
2718                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
2719                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
2720             }
2721             return [m autorelease];
2722         }
2723     }
2724
2725     return nil;
2726 }
2727
2728 - (IBAction)sdmenuhandler:(id)sender
2729 {
2730     NSString * identifier = [sender representedObject];
2731     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2732     {
2733         playlist_t * p_playlist = pl_Get( VLCIntf );
2734         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2735
2736         if (!sd_loaded)
2737             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2738         else
2739             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2740     }
2741 }
2742
2743 #pragma mark -
2744 #pragma mark Side Bar Delegate Methods
2745 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2746 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2747 {
2748     if ([[group identifier] isEqualToString:@"library"])
2749         return YES;
2750
2751     return NO;
2752 }
2753
2754 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2755 {
2756     playlist_t * p_playlist = pl_Get( VLCIntf );
2757
2758     NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2759     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2760
2761
2762     //Set the label text to represent the new selection
2763     if ([item sdtype] > -1 && [[item identifier] length] > 0)
2764     {
2765         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2766         if (!sd_loaded)
2767         {
2768             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2769         }
2770     }
2771
2772     [o_chosen_category_lbl setStringValue:[item title]];
2773
2774     if ([[item identifier] isEqualToString:@"playlist"])
2775     {
2776         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2777     }
2778     else if([[item identifier] isEqualToString:@"medialibrary"])
2779     {
2780         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2781     }
2782     else
2783     {
2784         playlist_item_t * pl_item;
2785         PL_LOCK;
2786         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2787         PL_UNLOCK;
2788         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2789     }
2790
2791     PL_LOCK;
2792     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2793         [self hideDropZone];
2794     else
2795         [self showDropZone];
2796     PL_UNLOCK;
2797
2798     if ([[item identifier] isEqualToString:@"podcast{longname=\"Podcasts\"}"])
2799         [self showPodcastControls];
2800     else
2801         [self hidePodcastControls];
2802 }
2803
2804 - (NSDragOperation)sourceList:(PXSourceList *)aSourceList validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
2805 {
2806     if ([[item identifier] isEqualToString:@"playlist"] || [[item identifier] isEqualToString:@"medialibrary"] )
2807     {
2808         NSPasteboard *o_pasteboard = [info draggingPasteboard];
2809         if ([[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] || [[o_pasteboard types] containsObject: NSFilenamesPboardType])
2810             return NSDragOperationGeneric;
2811     }
2812     return NSDragOperationNone;
2813 }
2814
2815 - (BOOL)sourceList:(PXSourceList *)aSourceList acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
2816 {
2817     NSPasteboard *o_pasteboard = [info draggingPasteboard];
2818
2819     playlist_t * p_playlist = pl_Get( VLCIntf );
2820     playlist_item_t *p_node;
2821
2822     if ([[item identifier] isEqualToString:@"playlist"])
2823         p_node = p_playlist->p_local_category;
2824     else
2825         p_node = p_playlist->p_ml_category;
2826
2827     if( [[o_pasteboard types] containsObject: NSFilenamesPboardType] )
2828     {
2829         NSArray *o_values = [[o_pasteboard propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
2830         NSUInteger count = [o_values count];
2831         NSMutableArray *o_array = [NSMutableArray arrayWithCapacity:count];
2832
2833         for( NSUInteger i = 0; i < count; i++)
2834         {
2835             NSDictionary *o_dic;
2836             char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
2837             if( !psz_uri )
2838                 continue;
2839
2840             o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
2841
2842             free( psz_uri );
2843
2844             [o_array addObject: o_dic];
2845         }
2846
2847         [[[VLCMain sharedInstance] playlist] appendNodeArray:o_array inNode: p_node atPos:-1 enqueue:YES];
2848         return YES;
2849     }
2850     else if( [[o_pasteboard types] containsObject: @"VLCPlaylistItemPboardType"] )
2851     {
2852         NSArray * array = [[[VLCMain sharedInstance] playlist] draggedItems];
2853
2854         NSUInteger count = [array count];
2855         playlist_item_t * p_item = NULL;
2856
2857         PL_LOCK;
2858         for( NSUInteger i = 0; i < count; i++ )
2859         {
2860             p_item = [[array objectAtIndex:i] pointerValue];
2861             if( !p_item ) continue;
2862             playlist_NodeAddCopy( p_playlist, p_item, p_node, PLAYLIST_END );
2863         }
2864         PL_UNLOCK;
2865
2866         return YES;
2867     }
2868     return NO;
2869 }
2870
2871 - (id)sourceList:(PXSourceList *)aSourceList persistentObjectForItem:(id)item
2872 {
2873     return [item identifier];
2874 }
2875
2876 - (id)sourceList:(PXSourceList *)aSourceList itemForPersistentObject:(id)object
2877 {
2878     /* the following code assumes for sakes of simplicity that only the top level
2879      * items are allowed to have children */
2880
2881     NSArray * array = [NSArray arrayWithArray: o_sidebaritems]; // read-only arrays are noticebly faster
2882     NSUInteger count = [array count];
2883     if (count < 1)
2884         return nil;
2885
2886     for (NSUInteger x = 0; x < count; x++)
2887     {
2888         id item = [array objectAtIndex: x]; // save one objc selector call
2889         if ([[item identifier] isEqualToString:object])
2890             return item;
2891     }
2892
2893     return nil;
2894 }
2895
2896 #pragma mark -
2897 #pragma mark Podcast
2898
2899 - (IBAction)addPodcast:(id)sender
2900 {
2901     [NSApp beginSheet:o_podcast_subscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2902 }
2903
2904 - (IBAction)addPodcastWindowAction:(id)sender
2905 {
2906     [o_podcast_subscribe_window orderOut:sender];
2907     [NSApp endSheet: o_podcast_subscribe_window];
2908
2909     if (sender == o_podcast_subscribe_ok_btn && [[o_podcast_subscribe_url_fld stringValue] length] > 0) {
2910         NSMutableString * podcastConf = [[NSMutableString alloc] init];
2911         if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL)
2912             [podcastConf appendFormat:@"%s|", config_GetPsz( VLCIntf, "podcast-urls" )];
2913
2914         [podcastConf appendString: [o_podcast_subscribe_url_fld stringValue]];
2915         config_PutPsz( VLCIntf, "podcast-urls", [podcastConf UTF8String] );
2916
2917         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2918         if( p_obj ) {
2919             var_SetString( p_obj, "podcast-urls", [podcastConf UTF8String] );
2920             vlc_object_release( p_obj );
2921         }
2922         [podcastConf release];
2923     }
2924 }
2925
2926 - (IBAction)removePodcast:(id)sender
2927 {
2928     if (config_GetPsz( VLCIntf, "podcast-urls" ) != NULL) {
2929         [o_podcast_unsubscribe_pop removeAllItems];
2930         [o_podcast_unsubscribe_pop addItemsWithTitles:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2931         [NSApp beginSheet:o_podcast_unsubscribe_window modalForWindow:self modalDelegate:self didEndSelector:NULL contextInfo:nil];
2932     }
2933 }
2934
2935 - (IBAction)removePodcastWindowAction:(id)sender
2936 {
2937     [o_podcast_unsubscribe_window orderOut:sender];
2938     [NSApp endSheet: o_podcast_unsubscribe_window];
2939
2940     if (sender == o_podcast_unsubscribe_ok_btn) {
2941         NSMutableArray * urls = [[NSMutableArray alloc] initWithArray:[[NSString stringWithUTF8String:config_GetPsz( VLCIntf, "podcast-urls" )] componentsSeparatedByString:@"|"]];
2942         [urls removeObjectAtIndex: [o_podcast_unsubscribe_pop indexOfSelectedItem]];
2943         config_PutPsz( VLCIntf, "podcast-urls", [[urls componentsJoinedByString:@"|"] UTF8String] );
2944         [urls release];
2945
2946         vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( VLCIntf->p_libvlc, "podcast" );
2947         if( p_obj ) {
2948             var_SetString( p_obj, "podcast-urls", config_GetPsz( VLCIntf, "podcast-urls" ) );
2949             vlc_object_release( p_obj );
2950         }
2951
2952         /* reload the podcast module, since it won't update its list when removing podcasts */
2953         playlist_t * p_playlist = pl_Get( VLCIntf );
2954         if( playlist_IsServicesDiscoveryLoaded( p_playlist, "podcast{longname=\"Podcasts\"}" ) ) {
2955             playlist_ServicesDiscoveryRemove( p_playlist, "podcast{longname=\"Podcasts\"}" );
2956             playlist_ServicesDiscoveryAdd( p_playlist, "podcast{longname=\"Podcasts\"}" );
2957             [o_playlist_table reloadData];
2958         }
2959
2960     }
2961 }
2962
2963 - (void)showPodcastControls
2964 {
2965     NSRect podcastViewDimensions = [o_podcast_view frame];
2966     NSRect rightSplitRect = [o_right_split_view frame];
2967     NSRect playlistTableRect = [o_playlist_table frame];
2968
2969     podcastViewDimensions.size.width = rightSplitRect.size.width;
2970     podcastViewDimensions.origin.x = podcastViewDimensions.origin.y = .0;
2971     [o_podcast_view setFrame:podcastViewDimensions];
2972
2973     playlistTableRect.origin.y = playlistTableRect.origin.y + podcastViewDimensions.size.height;
2974     playlistTableRect.size.height = playlistTableRect.size.height - podcastViewDimensions.size.height;
2975     [o_playlist_table setFrame:playlistTableRect];
2976     [o_playlist_table setNeedsDisplay:YES];
2977
2978     [o_right_split_view addSubview: o_podcast_view positioned: NSWindowAbove relativeTo: o_right_split_view];
2979     b_podcastView_displayed = YES;
2980 }
2981
2982 - (void)hidePodcastControls
2983 {
2984     if (b_podcastView_displayed) {
2985         NSRect podcastViewDimensions = [o_podcast_view frame];
2986         NSRect playlistTableRect = [o_playlist_table frame];
2987
2988         playlistTableRect.origin.y = playlistTableRect.origin.y - podcastViewDimensions.size.height;
2989         playlistTableRect.size.height = playlistTableRect.size.height + podcastViewDimensions.size.height;
2990
2991         [o_podcast_view removeFromSuperviewWithoutNeedingDisplay];
2992         [o_playlist_table setFrame: playlistTableRect];
2993         b_podcastView_displayed = NO;
2994     }
2995 }
2996
2997 #pragma mark -
2998 #pragma mark Accessibility stuff
2999
3000 - (NSArray *)accessibilityAttributeNames
3001 {
3002     if( !b_dark_interface )
3003         return [super accessibilityAttributeNames];
3004
3005     static NSMutableArray *attributes = nil;
3006     if ( attributes == nil ) {
3007         attributes = [[super accessibilityAttributeNames] mutableCopy];
3008         NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3009                                      NSAccessibilityCloseButtonAttribute,
3010                                      NSAccessibilityMinimizeButtonAttribute,
3011                                      NSAccessibilityZoomButtonAttribute,
3012                                      nil];
3013
3014         for( NSString *attribute in appendAttributes )
3015         {
3016             if( ![attributes containsObject:attribute] )
3017                 [attributes addObject:attribute];
3018         }
3019     }
3020     return attributes;
3021 }
3022
3023 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3024 {
3025     if( b_dark_interface )
3026     {
3027         VLCMainWindowTitleView *o_tbv = o_titlebar_view;
3028
3029         if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3030             return NSAccessibilityStandardWindowSubrole;
3031
3032         if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3033             return [[o_tbv closeButton] cell];
3034
3035         if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3036             return [[o_tbv minimizeButton] cell];
3037
3038         if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3039             return [[o_tbv zoomButton] cell];
3040     }
3041
3042     return [super accessibilityAttributeValue: o_attribute_name];
3043 }
3044
3045 - (id)detachedTitlebarView
3046 {
3047     return o_detached_titlebar_view;
3048 }
3049 @end
3050
3051 @implementation VLCDetachedVideoWindow
3052
3053 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
3054                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
3055 {
3056     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
3057     b_video_deco = var_InheritBool( VLCIntf, "video-deco" );
3058
3059     if (b_dark_interface || !b_video_deco)
3060     {
3061 #ifdef MAC_OS_X_VERSION_10_7
3062         if (OSX_LION)
3063             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
3064         else
3065             styleMask = NSBorderlessWindowMask;
3066 #else
3067         styleMask = NSBorderlessWindowMask;
3068 #endif
3069     }
3070
3071     self = [super initWithContentRect:contentRect styleMask:styleMask
3072                                   backing:backingType defer:flag];
3073
3074     /* we want to be moveable regardless of our style */
3075     [self setMovableByWindowBackground: YES];
3076
3077     /* we don't want this window to be restored on relaunch */
3078     if (OSX_LION)
3079         [self setRestorable:NO];
3080
3081     return self;
3082 }
3083
3084 - (void)awakeFromNib
3085 {
3086     [self setAcceptsMouseMovedEvents: YES];
3087
3088     if (b_dark_interface)
3089     {
3090         [self setBackgroundColor: [NSColor clearColor]];
3091         [self setOpaque: NO];
3092         [self display];
3093         [self setHasShadow:NO];
3094         [self setHasShadow:YES];
3095     }
3096 }
3097
3098 - (IBAction)fullscreen:(id)sender
3099 {
3100     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
3101 }
3102
3103 - (void)performClose:(id)sender
3104 {
3105     if (b_dark_interface || !b_video_deco)
3106         [[VLCMainWindow sharedInstance] performClose: sender];
3107     else
3108         [super performClose: sender];
3109 }
3110
3111 - (void)performMiniaturize:(id)sender
3112 {
3113     if (b_dark_interface || !b_video_deco)
3114         [self miniaturize: sender];
3115     else
3116         [super performMiniaturize: sender];
3117 }
3118
3119 - (void)performZoom:(id)sender
3120 {
3121     if (b_dark_interface || !b_video_deco)
3122         [self customZoom: sender];
3123     else
3124         [super performZoom: sender];
3125 }
3126
3127 - (void)zoom:(id)sender
3128 {
3129     if (b_dark_interface || !b_video_deco)
3130         [self customZoom: sender];
3131     else
3132         [super zoom: sender];
3133 }
3134
3135 - (BOOL)canBecomeKeyWindow
3136 {
3137     return YES;
3138 }
3139
3140 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
3141 {
3142     SEL s_menuAction = [menuItem action];
3143
3144     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
3145         return YES;
3146
3147     return [super validateMenuItem:menuItem];
3148 }
3149
3150 /**
3151  * Given a proposed frame rectangle, return a modified version
3152  * which will fit inside the screen.
3153  *
3154  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3155  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3156  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3157  *    Copyright (C) 1996 Free Software Foundation, Inc.
3158  */
3159 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
3160 {
3161     NSRect screenRect = [screen visibleFrame];
3162     float difference;
3163
3164     /* Move top edge of the window inside the screen */
3165     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
3166     if (difference > 0)
3167     {
3168         frameRect.origin.y -= difference;
3169     }
3170
3171     /* If the window is resizable, resize it (if needed) so that the
3172      bottom edge is on the screen or can be on the screen when the user moves
3173      the window */
3174     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
3175     if (_styleMask & NSResizableWindowMask)
3176     {
3177         float difference2;
3178
3179         difference2 = screenRect.origin.y - frameRect.origin.y;
3180         difference2 -= difference;
3181         // Take in account the space between the top of window and the top of the
3182         // screen which can be used to move the bottom of the window on the screen
3183         if (difference2 > 0)
3184         {
3185             frameRect.size.height -= difference2;
3186             frameRect.origin.y += difference2;
3187         }
3188
3189         /* Ensure that resizing doesn't makewindow smaller than minimum */
3190         difference2 = [self minSize].height - frameRect.size.height;
3191         if (difference2 > 0)
3192         {
3193             frameRect.size.height += difference2;
3194             frameRect.origin.y -= difference2;
3195         }
3196     }
3197
3198     return frameRect;
3199 }
3200
3201 #define DIST 3
3202
3203 /**
3204  Zooms the receiver.   This method calls the delegate method
3205  windowShouldZoom:toFrame: to determine if the window should
3206  be allowed to zoom to full screen.
3207  *
3208  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
3209  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
3210  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
3211  *    Copyright (C) 1996 Free Software Foundation, Inc.
3212  */
3213 - (void) customZoom: (id)sender
3214 {
3215     NSRect maxRect = [[self screen] visibleFrame];
3216     NSRect currentFrame = [self frame];
3217
3218     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
3219     {
3220         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
3221     }
3222
3223     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
3224
3225     // Compare the new frame with the current one
3226     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
3227         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
3228         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
3229         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
3230     {
3231         // Already in zoomed mode, reset user frame, if stored
3232         if ([self frameAutosaveName] != nil)
3233         {
3234             [self setFrame: previousSavedFrame display: YES animate: YES];
3235             [self saveFrameUsingName: [self frameAutosaveName]];
3236         }
3237         return;
3238     }
3239
3240     if ([self frameAutosaveName] != nil)
3241     {
3242         [self saveFrameUsingName: [self frameAutosaveName]];
3243         previousSavedFrame = [self frame];
3244     }
3245
3246     [self setFrame: maxRect display: YES animate: YES];
3247 }
3248
3249 - (NSArray *)accessibilityAttributeNames
3250 {
3251     if( !b_dark_interface )
3252         return [super accessibilityAttributeNames];
3253
3254     static NSMutableArray *attributes = nil;
3255     if ( attributes == nil ) {
3256         attributes = [[super accessibilityAttributeNames] mutableCopy];
3257         NSArray *appendAttributes = [NSArray arrayWithObjects: NSAccessibilitySubroleAttribute,
3258                                      NSAccessibilityCloseButtonAttribute,
3259                                      NSAccessibilityMinimizeButtonAttribute,
3260                                      NSAccessibilityZoomButtonAttribute,
3261                                      nil];
3262
3263         for( NSString *attribute in appendAttributes )
3264         {
3265             if( ![attributes containsObject:attribute] )
3266                 [attributes addObject:attribute];
3267         }
3268     }
3269     return attributes;
3270 }
3271
3272 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
3273 {
3274     if( b_dark_interface )
3275     {
3276         VLCMainWindowTitleView *o_tbv = [[VLCMainWindow sharedInstance] detachedTitlebarView];
3277
3278         if( [o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute] )
3279             return NSAccessibilityStandardWindowSubrole;
3280
3281         if( [o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute] )
3282             return [[o_tbv closeButton] cell];
3283
3284         if( [o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute] )
3285             return [[o_tbv minimizeButton] cell];
3286
3287         if( [o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute] )
3288             return [[o_tbv zoomButton] cell];
3289     }
3290
3291     return [super accessibilityAttributeValue: o_attribute_name];
3292 }
3293
3294 @end