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