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