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