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