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