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