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