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