]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
36c3bc6ba57bc4913b80eaa3c2c56489c6e43f27
[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 <vlc_playlist.h>
38 #import <vlc_aout_intf.h>
39 #import <vlc_url.h>
40 #import <vlc_strings.h>
41 #import <vlc_services_discovery.h>
42 #import <vlc_aout_intf.h>
43
44 @implementation VLCMainWindow
45 static VLCMainWindow *_o_sharedInstance = nil;
46
47 + (VLCMainWindow *)sharedInstance
48 {
49     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
50 }
51
52 #pragma mark -
53 #pragma mark Initialization
54
55 - (id)init
56 {
57     if( _o_sharedInstance)
58     {
59         [self dealloc];
60         return _o_sharedInstance;
61     }
62     else
63     {
64         o_fspanel = [[VLCFSPanel alloc] init];
65         _o_sharedInstance = [super init];
66     }
67
68     return _o_sharedInstance;
69 }
70
71 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
72                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
73 {
74     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
75
76     if (b_dark_interface)
77     {
78 #ifdef MAC_OS_X_VERSION_10_7
79         if (OSX_LION)
80             styleMask = NSBorderlessWindowMask | NSResizableWindowMask;
81         else
82             styleMask = NSBorderlessWindowMask;
83 #else
84         styleMask = NSBorderlessWindowMask;
85 #endif
86     }
87
88     self = [super initWithContentRect:contentRect styleMask:styleMask
89                               backing:backingType defer:flag];
90
91     [[VLCMain sharedInstance] updateTogglePlaylistState];
92
93     /* we want to be moveable regardless of our style */
94     [self setMovableByWindowBackground: YES];
95
96     /* we don't want this window to be restored on relaunch */
97     if (OSX_LION)
98         [self setRestorable:NO];
99
100     return self;
101 }
102
103 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
104 {
105     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
106      so we perform the menu equivalent now. */
107     if([[NSApp mainMenu] performKeyEquivalent:o_event])
108         return TRUE;
109
110     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] || [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
111 }
112
113 - (void)dealloc
114 {
115     if (b_dark_interface)
116         [o_color_backdrop release];
117
118     [[NSNotificationCenter defaultCenter] removeObserver: self];
119     [o_sidebaritems release];
120     [super dealloc];
121 }
122
123 - (void)awakeFromNib
124 {
125     /* setup the styled interface */
126     b_nativeFullscreenMode = NO;
127 #ifdef MAC_OS_X_VERSION_10_7
128     if( config_GetInt( VLCIntf, "embedded-video" ))
129         b_nativeFullscreenMode = config_GetInt( VLCIntf, "macosx-nativefullscreenmode" );
130 #endif
131     i_lastShownVolume = -1;
132     t_hide_mouse_timer = nil;
133
134     [o_play_btn setToolTip: _NS("Play/Pause")];
135     [o_bwd_btn setToolTip: _NS("Backward")];
136     [o_fwd_btn setToolTip: _NS("Forward")];
137     [o_stop_btn setToolTip: _NS("Stop")];
138     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
139     [o_repeat_btn setToolTip: _NS("Repeat")];
140     [o_shuffle_btn setToolTip: _NS("Shuffle")];
141     [o_effects_btn setToolTip: _NS("Effects")];
142     [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
143     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
144     [o_volume_sld setToolTip: _NS("Volume")];
145     [o_volume_down_btn setToolTip: _NS("Mute")];
146     [o_volume_up_btn setToolTip: _NS("Full Volume")];
147     [o_time_sld setToolTip: _NS("Position")];
148     [o_dropzone_btn setTitle: _NS("Open media...")];
149     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
150
151     if (!b_dark_interface) {
152         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottom-background"] middle: [NSImage imageNamed:@"bottom-background"] right: [NSImage imageNamed:@"bottom-background"]];
153         [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
154         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
155         o_play_img = [[NSImage imageNamed:@"play"] retain];
156         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
157         o_pause_img = [[NSImage imageNamed:@"pause"] retain];
158         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
159         [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
160         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
161         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
162         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
163         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist"]];
164         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed"]];
165         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
166         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
167         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
168         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
169         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
170         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
171         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
172         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
173         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
174         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
175         [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"]];
176         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
177         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
178         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
179         if (OSX_LION && b_nativeFullscreenMode)
180         {
181             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
182             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
183         }
184         else
185         {
186             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
187             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
188         }
189         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
190         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
191         [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
192     }
193     else
194     {
195         [o_bottombar_view setImagesLeft: [NSImage imageNamed:@"bottomdark-left"] middle: [NSImage imageNamed:@"bottom-background_dark"] right: [NSImage imageNamed:@"bottomdark-right"]];
196         [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
197         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
198         o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
199         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
200         o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
201         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
202         [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
203         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
204         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
205         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
206         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
207         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
208         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
209         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
210         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
211         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
212         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
213         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
214         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
215         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
216         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
217         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
218         [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"]];
219         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
220         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
221         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
222         if (OSX_LION && b_nativeFullscreenMode)
223         {
224             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
225             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
226         }
227         else
228         {
229             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
230             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];            
231         }
232         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
233         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
234         [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"]];
235     }
236     [o_repeat_btn setImage: o_repeat_img];
237     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
238     [o_shuffle_btn setImage: o_shuffle_img];
239     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
240     [o_play_btn setImage: o_play_img];
241     [o_play_btn setAlternateImage: o_play_pressed_img];
242     BOOL b_mute = ![[VLCCoreInteraction sharedInstance] isMuted];
243     [o_volume_sld setEnabled: b_mute];
244     [o_volume_up_btn setEnabled: b_mute];
245
246     /* interface builder action */
247     [self setDelegate: self];
248     [self setExcludedFromWindowsMenu: YES];
249     [self setAcceptsMouseMovedEvents: YES];
250     // Set that here as IB seems to be buggy
251     if (b_dark_interface)
252         [self setContentMinSize:NSMakeSize(604., (288. + [o_titlebar_view frame].size.height))];
253     else
254         [self setContentMinSize:NSMakeSize(604., 288.)];
255     [self setTitle: _NS("VLC media player")];
256     [o_time_fld setAlignment: NSCenterTextAlignment];
257     [o_time_fld setNeedsDisplay:YES];
258     b_dropzone_active = YES;
259     o_temp_view = [[NSView alloc] init];
260     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
261     [o_dropzone_view setFrame: [o_playlist_table frame]];
262     [o_left_split_view setFrame: [o_sidebar_view frame]];
263     if (OSX_LION && b_nativeFullscreenMode)
264     {
265         NSRect frame;
266         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
267         float f_width = [o_fullscreen_btn frame].size.width;
268
269         #define moveItem( item ) \
270         frame = [item frame]; \
271         frame.origin.x = f_width + frame.origin.x; \
272         [item setFrame: frame]
273
274         moveItem( o_effects_btn );
275         moveItem( o_volume_up_btn );
276         moveItem( o_volume_sld );
277         moveItem( o_volume_track_view );
278         moveItem( o_volume_down_btn );
279         moveItem( o_time_fld );
280         #undef moveItem
281
282         #define enlargeItem( item ) \
283         frame = [item frame]; \
284         frame.size.width = f_width + frame.size.width; \
285         [item setFrame: frame]
286
287         enlargeItem( o_time_sld );
288         enlargeItem( o_progress_bar );
289         enlargeItem( o_time_sld_background );
290         enlargeItem( o_time_sld_fancygradient_view );
291         #undef enlargeItem
292
293         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
294     }
295     else
296         [o_titlebar_view setFullscreenButtonHidden: YES];
297
298     if (OSX_LION)
299     {
300         /* the default small size of the search field is slightly different on Lion, let's work-around that */
301         NSRect frame;
302         frame = [o_search_fld frame];
303         frame.origin.y = frame.origin.y + 2.0;
304         frame.size.height = frame.size.height - 1.0;
305         [o_search_fld setFrame: frame];
306     }
307
308     /* create the sidebar */
309     o_sidebaritems = [[NSMutableArray alloc] init];
310     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
311     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
312     [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
313     SideBarItem *medialibraryItem = [SideBarItem itemWithTitle:_NS("Media Library") identifier:@"medialibrary"];
314     [medialibraryItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
315     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
316     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
317     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
318     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
319
320     /* SD subnodes, inspired by the Qt4 intf */
321     char **ppsz_longnames;
322     int *p_categories;
323     char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
324     if (!ppsz_names)
325         msg_Err( VLCIntf, "no sd item found" ); //TODO
326     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
327     int *p_category = p_categories;
328     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
329     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
330     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
331     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
332     NSString *o_identifier;
333     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
334     {
335         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
336         switch (*p_category) {
337             case SD_CAT_INTERNET:
338                 {
339                     [internetItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
340                     if (!strncmp( *ppsz_name, "podcast", 7 ))
341                         [internetItems removeLastObject]; // we don't support podcasts at this point (see #6017)
342 //                        [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
343                     else
344                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
345                     [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
346                     [[internetItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
347                 }
348                 break;
349             case SD_CAT_DEVICES:
350                 {
351                     [devicesItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
352                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
353                     [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
354                     [[devicesItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
355                 }
356                 break;
357             case SD_CAT_LAN:
358                 {
359                     [lanItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
360                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
361                     [[lanItems lastObject] setSdtype: SD_CAT_LAN];
362                     [[lanItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
363                 }
364                 break;
365             case SD_CAT_MYCOMPUTER:
366                 {
367                     [mycompItems addObject: [SideBarItem itemWithTitle: _NS(*ppsz_longname) identifier: o_identifier]];
368                     if (!strncmp( *ppsz_name, "video_dir", 9 ))
369                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
370                     else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
371                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
372                     else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
373                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
374                     else
375                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
376                     [[mycompItems lastObject] setUntranslatedTitle: [NSString stringWithUTF8String: *ppsz_longname]];
377                     [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
378                 }
379                 break;
380             default:
381                 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
382                 break;
383         }
384
385         free( *ppsz_name );
386         free( *ppsz_longname );
387     }
388     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
389     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
390     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
391     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
392     [mycompItems release];
393     [devicesItems release];
394     [lanItems release];
395     [internetItems release];
396     free( ppsz_names );
397     free( ppsz_longnames );
398     free( p_categories );
399
400     [libraryItem setChildren: [NSArray arrayWithObjects: playlistItem, medialibraryItem, nil]];
401     [o_sidebaritems addObject: libraryItem];
402     if ([mycompItem hasChildren])
403         [o_sidebaritems addObject: mycompItem];
404     if ([devicesItem hasChildren])
405         [o_sidebaritems addObject: devicesItem];
406     if ([lanItem hasChildren])
407         [o_sidebaritems addObject: lanItem];
408     if ([internetItem hasChildren])
409         [o_sidebaritems addObject: internetItem];
410
411     [o_sidebar_view reloadData];
412     NSUInteger i_sidebaritem_count = [o_sidebaritems count];
413     for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
414         [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
415     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
416
417     if( b_dark_interface )
418     {
419         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
420         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
421
422         [self setBackgroundColor: [NSColor clearColor]];
423         [self setOpaque: NO];
424         [self display];
425         [self setHasShadow:NO];
426         [self setHasShadow:YES];
427
428         NSRect winrect = [self frame];
429         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
430
431         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
432                                               winrect.size.width, f_titleBarHeight )];
433         [[self contentView] addSubview: o_titlebar_view];
434
435         [self setFrame: winrect display:YES animate:YES];
436         previousSavedFrame = winrect;
437         winrect = [o_split_view frame];
438         winrect.size.height = winrect.size.height - f_titleBarHeight;
439         [o_split_view setFrame: winrect];
440         [o_video_view setFrame: winrect];
441
442         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
443         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
444         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
445     }
446     else
447     {
448         NSRect frame;
449         frame = [o_time_sld_fancygradient_view frame];
450         frame.size.height = frame.size.height - 1;
451         frame.origin.y = frame.origin.y + 1;
452         [o_time_sld_fancygradient_view setFrame: frame];
453
454         [o_video_view setFrame: [o_split_view frame]];
455         [o_playlist_table setBorderType: NSNoBorder];
456         [o_sidebar_scrollview setBorderType: NSNoBorder];
457     }
458
459     if (OSX_LION)
460         [o_resize_view setImage: NULL];
461
462     if ([self styleMask] & NSResizableWindowMask)
463         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
464
465     if (OSX_LEOPARD)
466         [o_time_sld_fancygradient_view removeFromSuperviewWithoutNeedingDisplay];
467
468     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
469     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
470     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification object: nil];
471     [[VLCMain sharedInstance] playbackModeUpdated];
472 }
473
474 #pragma mark -
475 #pragma mark Button Actions
476
477 - (IBAction)play:(id)sender
478 {
479     [[VLCCoreInteraction sharedInstance] play];
480 }
481
482 - (void)resetPreviousButton
483 {
484     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
485         // seems like no further event occured, so let's switch the playback item
486         [[VLCCoreInteraction sharedInstance] previous];
487         just_triggered_previous = NO;
488     }
489 }
490
491 - (void)resetBackwardSkip
492 {
493     // the user stopped skipping, so let's allow him to change the item
494     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
495         just_triggered_previous = NO;
496 }
497
498 - (IBAction)bwd:(id)sender
499 {
500     if(!just_triggered_previous)
501     {
502         just_triggered_previous = YES;
503         [self performSelector:@selector(resetPreviousButton)
504                    withObject: NULL
505                    afterDelay:0.40];
506     }
507     else
508     {
509         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.12 )
510         {
511             // we just skipped 3 "continous" events, otherwise we are too fast
512             [[VLCCoreInteraction sharedInstance] backward];
513             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
514             [self performSelector:@selector(resetBackwardSkip)
515                        withObject: NULL
516                        afterDelay:0.40];
517         }
518     }
519 }
520
521 - (void)resetNextButton
522 {
523     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
524         // seems like no further event occured, so let's switch the playback item
525         [[VLCCoreInteraction sharedInstance] next];
526         just_triggered_next = NO;
527     }
528 }
529
530 - (void)resetForwardSkip
531 {
532     // the user stopped skipping, so let's allow him to change the item
533     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
534         just_triggered_next = NO;
535 }
536
537 - (IBAction)fwd:(id)sender
538 {
539    if(!just_triggered_next)
540     {
541         just_triggered_next = YES;
542         [self performSelector:@selector(resetNextButton)
543                    withObject: NULL
544                    afterDelay:0.40];
545     }
546     else
547     {
548         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.12 )
549         {
550             // we just skipped 3 "continous" events, otherwise we are too fast
551             [[VLCCoreInteraction sharedInstance] forward];
552             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
553             [self performSelector:@selector(resetForwardSkip)
554                        withObject: NULL
555                        afterDelay:0.40];
556         }
557     }
558 }
559
560 - (IBAction)stop:(id)sender
561 {
562     [[VLCCoreInteraction sharedInstance] stop];
563 }
564
565 - (IBAction)togglePlaylist:(id)sender
566 {
567     if (b_dropzone_active && ![[VLCMain sharedInstance] activeVideoPlayback])
568     {
569         b_dropzone_active = NO;
570         [self hideDropZone];
571     }
572
573     if (!b_nonembedded)
574     {
575         if ([o_video_view isHidden] && [[VLCMain sharedInstance] activeVideoPlayback]) {
576             [o_split_view setHidden: YES];
577             [o_video_view setHidden: NO];
578             [self makeFirstResponder: o_video_view];
579         }
580         else
581         {
582             [o_video_view setHidden: YES];
583             [o_split_view setHidden: NO];
584             [self makeFirstResponder: nil];
585         }
586     }
587     else
588     {
589         [o_split_view setHidden: NO];
590         [o_playlist_table setHidden: NO];
591         [o_video_view setHidden: ![[VLCMain sharedInstance] activeVideoPlayback]];
592     }
593 }
594
595 - (void)setRepeatOne
596 {
597     [o_repeat_btn setImage: o_repeat_one_img];
598     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];   
599 }
600
601 - (void)setRepeatAll
602 {
603     [o_repeat_btn setImage: o_repeat_all_img];
604     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
605 }
606
607 - (void)setRepeatOff
608 {
609     [o_repeat_btn setImage: o_repeat_img];
610     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
611 }
612
613 - (IBAction)repeat:(id)sender
614 {
615     vlc_value_t looping,repeating;
616     intf_thread_t * p_intf = VLCIntf;
617     playlist_t * p_playlist = pl_Get( p_intf );
618
619     var_Get( p_playlist, "repeat", &repeating );
620     var_Get( p_playlist, "loop", &looping );
621
622     if( !repeating.b_bool && !looping.b_bool )
623     {
624         /* was: no repeating at all, switching to Repeat One */
625         [[VLCCoreInteraction sharedInstance] repeatOne];
626         [self setRepeatOne];
627     }
628     else if( repeating.b_bool && !looping.b_bool )
629     {
630         /* was: Repeat One, switching to Repeat All */
631         [[VLCCoreInteraction sharedInstance] repeatAll];
632         [self setRepeatAll];
633     }
634     else
635     {
636         /* was: Repeat All or bug in VLC, switching to Repeat Off */
637         [[VLCCoreInteraction sharedInstance] repeatOff];
638         [self setRepeatOff];
639     }
640 }
641
642 - (void)setShuffle
643 {
644     bool b_value;
645     playlist_t *p_playlist = pl_Get( VLCIntf );
646     b_value = var_GetBool( p_playlist, "random" );
647         if(b_value) {
648         [o_shuffle_btn setImage: o_shuffle_on_img];
649         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
650     }
651     else
652     {
653         [o_shuffle_btn setImage: o_shuffle_img];
654         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
655     }
656 }
657
658 - (IBAction)shuffle:(id)sender
659 {
660     [[VLCCoreInteraction sharedInstance] shuffle];
661     [self setShuffle];
662 }
663
664 - (IBAction)timeSliderAction:(id)sender
665 {
666     float f_updated;
667     input_thread_t * p_input;
668
669     switch( [[NSApp currentEvent] type] )
670     {
671         case NSLeftMouseUp:
672         case NSLeftMouseDown:
673         case NSLeftMouseDragged:
674             f_updated = [sender floatValue];
675             break;
676
677         default:
678             return;
679     }
680     p_input = pl_CurrentInput( VLCIntf );
681     if( p_input != NULL )
682     {
683         vlc_value_t time;
684         vlc_value_t pos;
685         NSString * o_time;
686         char psz_time[MSTRTIME_MAX_SIZE];
687
688         pos.f_float = f_updated / 10000.;
689         var_Set( p_input, "position", pos );
690         [o_time_sld setFloatValue: f_updated];
691
692         var_Get( p_input, "time", &time );
693
694         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
695         if( [o_time_fld timeRemaining] && dur != -1 )
696         {
697             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
698         }
699         else
700             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
701
702         [o_time_fld setStringValue: o_time];
703         [o_fspanel setStreamPos: f_updated andTime: o_time];
704         vlc_object_release( p_input );
705     }
706 }
707
708 - (IBAction)volumeAction:(id)sender
709 {
710     if (sender == o_volume_sld)
711         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
712     else if (sender == o_volume_down_btn)
713     {
714         [[VLCCoreInteraction sharedInstance] mute];
715         [o_volume_sld setIntValue: 0];
716         BOOL b_mute = ![[VLCCoreInteraction sharedInstance] isMuted];
717         [o_volume_sld setEnabled: b_mute];
718         [o_volume_up_btn setEnabled: b_mute];
719     }
720     else
721         [[VLCCoreInteraction sharedInstance] setVolume: AOUT_VOLUME_MAX];
722 }
723
724 - (IBAction)effects:(id)sender
725 {
726     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
727 }
728
729 - (IBAction)fullscreen:(id)sender
730 {
731     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
732 }
733
734 - (IBAction)dropzoneButtonAction:(id)sender
735 {
736     [[[VLCMain sharedInstance] open] openFileGeneric];
737 }
738
739 #pragma mark -
740 #pragma mark overwritten default functionality
741 - (BOOL)canBecomeKeyWindow
742 {
743     return YES;
744 }
745
746 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
747 {
748         SEL s_menuAction = [menuItem action];
749         if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
750         return YES;
751
752         return [super validateMenuItem:menuItem];
753 }
754
755 - (BOOL)isMainWindow
756 {
757         return YES;
758 }
759
760 - (void)setTitle:(NSString *)title
761 {
762     if (b_dark_interface)
763         [o_titlebar_view setWindowTitle: title];
764     if (b_nonembedded && [[VLCMain sharedInstance] activeVideoPlayback])
765         [o_nonembedded_window setTitle: title];
766     [super setTitle: title];
767 }
768
769 - (void)performClose:(id)sender
770 {
771     if (b_dark_interface)
772     {
773         [self orderOut: sender];
774         [[VLCCoreInteraction sharedInstance] stop];
775     }
776     else
777         [super performClose: sender];
778 }
779
780 - (void)performMiniaturize:(id)sender
781 {
782     if (b_dark_interface)
783     {
784         [self miniaturize: sender];
785         if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
786         {
787             if ([[VLCMain sharedInstance] activeVideoPlayback])
788                 [[VLCCoreInteraction sharedInstance] pause];
789         }
790     }
791     else
792         [super performMiniaturize: sender];
793 }
794
795 - (void)performZoom:(id)sender
796 {
797     if (b_dark_interface)
798         [self customZoom: sender];
799     else
800         [super performZoom: sender];
801 }
802
803 - (void)zoom:(id)sender
804 {
805     if (b_dark_interface)
806         [self customZoom: sender];
807     else
808         [super zoom: sender];
809 }
810
811 /**
812  * Given a proposed frame rectangle, return a modified version
813  * which will fit inside the screen.
814  *
815  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
816  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
817  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
818  *    Copyright (C) 1996 Free Software Foundation, Inc.
819  */
820 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
821 {
822     NSRect screenRect = [screen visibleFrame];
823     float difference;
824
825     /* Move top edge of the window inside the screen */
826     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
827     if (difference > 0)
828     {
829         frameRect.origin.y -= difference;
830     }
831
832     /* If the window is resizable, resize it (if needed) so that the
833      bottom edge is on the screen or can be on the screen when the user moves
834      the window */
835     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
836     if (_styleMask & NSResizableWindowMask)
837     {
838         float difference2;
839
840         difference2 = screenRect.origin.y - frameRect.origin.y;
841         difference2 -= difference;
842         // Take in account the space between the top of window and the top of the 
843         // screen which can be used to move the bottom of the window on the screen
844         if (difference2 > 0)
845         {
846             frameRect.size.height -= difference2;
847             frameRect.origin.y += difference2;
848         }
849
850         /* Ensure that resizing doesn't makewindow smaller than minimum */
851         difference2 = [self minSize].height - frameRect.size.height;
852         if (difference2 > 0)
853         {
854             frameRect.size.height += difference2;
855             frameRect.origin.y -= difference2;
856         }
857     }
858
859     return frameRect;
860 }
861
862 #define DIST 3
863
864 /**
865  Zooms the receiver.   This method calls the delegate method
866  windowShouldZoom:toFrame: to determine if the window should
867  be allowed to zoom to full screen.
868  *
869  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
870  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
871  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
872  *    Copyright (C) 1996 Free Software Foundation, Inc.
873  */
874 - (void) customZoom: (id)sender
875 {
876     NSRect maxRect = [[self screen] visibleFrame];
877     NSRect currentFrame = [self frame];
878
879     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
880     {
881         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
882     }
883
884     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
885
886     // Compare the new frame with the current one
887     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
888         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
889         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
890         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
891     {
892         // Already in zoomed mode, reset user frame, if stored
893         if ([self frameAutosaveName] != nil)
894         {
895             [self setFrame: previousSavedFrame display: YES animate: YES];
896             [self saveFrameUsingName: [self frameAutosaveName]];
897         }
898         return;
899     }
900
901     if ([self frameAutosaveName] != nil)
902     {
903         [self saveFrameUsingName: [self frameAutosaveName]];
904         previousSavedFrame = [self frame];
905     }
906
907     [self setFrame: maxRect display: YES animate: YES];
908 }
909
910 - (void)windowResizedOrMoved:(NSNotification *)notification
911 {
912     [self saveFrameUsingName: [self frameAutosaveName]];
913 }
914
915 - (void)applicationWillTerminate:(NSNotification *)notification
916 {
917     if( config_GetInt( VLCIntf, "macosx-autosave-volume" ))
918         config_PutInt( VLCIntf->p_libvlc, "volume", i_lastShownVolume );
919
920     [self saveFrameUsingName: [self frameAutosaveName]];
921 }
922
923 - (void)someWindowWillClose:(NSNotification *)notification
924 {
925     if([notification object] == o_nonembedded_window || [notification object] == self)
926         [[VLCCoreInteraction sharedInstance] stop];
927 }
928
929 - (void)someWindowWillMiniaturize:(NSNotification *)notification
930 {
931     if (config_GetInt( VLCIntf, "macosx-pause-minimized" ))
932     {
933         if([notification object] == o_nonembedded_window || [notification object] == self)
934         {
935             if([[VLCMain sharedInstance] activeVideoPlayback])
936                 [[VLCCoreInteraction sharedInstance] pause];
937         }
938     }
939 }
940
941 #pragma mark -
942 #pragma mark Update interface and respond to foreign events
943 - (void)showDropZone
944 {
945     b_dropzone_active = YES;
946     [o_right_split_view addSubview: o_dropzone_view];
947     [o_dropzone_view setFrame: [o_playlist_table frame]];
948     [[o_playlist_table animator] setHidden:YES];
949 }
950
951 - (void)hideDropZone
952 {
953     [o_dropzone_view removeFromSuperview];
954     [[o_playlist_table animator] setHidden: NO];
955 }
956
957 - (void)updateTimeSlider
958 {
959     input_thread_t * p_input;
960     p_input = pl_CurrentInput( VLCIntf );
961     if( p_input )
962     {
963         vlc_value_t time;
964         NSString * o_time;
965         vlc_value_t pos;
966         char psz_time[MSTRTIME_MAX_SIZE];
967         float f_updated;
968
969         var_Get( p_input, "position", &pos );
970         f_updated = 10000. * pos.f_float;
971         [o_time_sld setFloatValue: f_updated];
972
973         var_Get( p_input, "time", &time );
974
975         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
976         if( [o_time_fld timeRemaining] && dur != -1 )
977         {
978             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
979         }
980         else
981             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
982
983         if (dur == -1) {
984             [o_time_sld setEnabled: NO];
985             [o_time_sld setHidden: YES];
986             [o_time_sld_fancygradient_view setHidden: YES];
987         } else {
988             [o_time_sld setEnabled: YES];
989             [o_time_sld setHidden: NO];
990             [o_time_sld_fancygradient_view setHidden: NO];
991         }
992
993         [o_time_fld setStringValue: o_time];
994         [o_time_fld setNeedsDisplay:YES];
995         [o_fspanel setStreamPos: f_updated andTime: o_time];
996         vlc_object_release( p_input );
997     }
998     else
999     {
1000         [o_time_sld setFloatValue: 0.0];
1001         [o_time_fld setStringValue: @"00:00"];
1002         [o_time_sld setEnabled: NO];
1003         [o_time_sld setHidden: YES];
1004         [o_time_sld_fancygradient_view setHidden: YES];
1005     }
1006 }
1007
1008 - (void)updateVolumeSlider
1009 {
1010     audio_volume_t i_volume;
1011     playlist_t * p_playlist = pl_Get( VLCIntf );
1012
1013     i_volume = aout_VolumeGet( p_playlist );
1014
1015     if( i_volume != i_lastShownVolume )
1016     {
1017         i_lastShownVolume = i_volume;
1018         [o_volume_sld setIntValue: i_volume];
1019         [o_fspanel setVolumeLevel: i_volume];
1020     }
1021 }
1022
1023 - (void)updateName
1024 {
1025     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1026     input_thread_t * p_input;
1027     p_input = pl_CurrentInput( VLCIntf );
1028     if( p_input )
1029     {
1030         NSString *aString;
1031         char *format = var_InheritString( VLCIntf, "input-title-format" );
1032         char *formated = str_format_meta( p_input, format );
1033         free( format );
1034         aString = [NSString stringWithUTF8String:formated];
1035         free( formated );
1036
1037         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1038
1039         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1040         if ([o_url isFileURL])
1041             [self setRepresentedURL: o_url];
1042         else
1043             [self setRepresentedURL: nil];
1044         free( uri );
1045
1046         if ([aString isEqualToString:@""])
1047         {
1048             if ([o_url isFileURL])
1049                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1050             else
1051                 aString = [o_url absoluteString];
1052         }
1053
1054         [self setTitle: aString];
1055         [o_fspanel setStreamTitle: aString];
1056         vlc_object_release( p_input );
1057     }
1058     else
1059     {
1060         [self setTitle: _NS("VLC media player")];
1061         [self setRepresentedURL: nil];
1062     }
1063
1064     [o_pool release];
1065 }
1066
1067 - (void)updateWindow
1068 {
1069     bool b_input = false;
1070     bool b_plmul = false;
1071     bool b_control = false;
1072     bool b_seekable = false;
1073     bool b_chapters = false;
1074
1075     playlist_t * p_playlist = pl_Get( VLCIntf );
1076
1077     PL_LOCK;
1078     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1079     PL_UNLOCK;
1080
1081     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1082
1083     bool b_buffering = NO;
1084
1085     if( ( b_input = ( p_input != NULL ) ) )
1086     {
1087         /* seekable streams */
1088         cachedInputState = input_GetState( p_input );
1089         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1090             b_buffering = YES;
1091
1092         /* seekable streams */
1093         b_seekable = var_GetBool( p_input, "can-seek" );
1094
1095         /* check whether slow/fast motion is possible */
1096         b_control = var_GetBool( p_input, "can-rate" );
1097
1098         /* chapters & titles */
1099         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1100
1101         if (( cachedInputState == PLAYING_S || b_buffering == YES ) && [[VLCMain sharedInstance] activeVideoPlayback] )
1102             [[o_video_view window] makeKeyAndOrderFront: nil];
1103
1104         vlc_object_release( p_input );
1105     }
1106
1107     if( b_buffering )
1108     {
1109         [o_progress_bar startAnimation:self];
1110         [o_progress_bar setIndeterminate:YES];
1111         [o_progress_bar setHidden:NO];
1112     } else {
1113         [o_progress_bar stopAnimation:self];
1114         [o_progress_bar setHidden:YES];
1115     }
1116
1117     [o_stop_btn setEnabled: b_input];
1118     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1119     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1120     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1121
1122     [o_time_sld setEnabled: b_seekable];
1123     [self updateTimeSlider];
1124     [o_fspanel setSeekable: b_seekable];
1125
1126     PL_LOCK;
1127     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
1128         [self hideDropZone];
1129     else
1130         [self showDropZone];
1131     PL_UNLOCK;
1132     [o_sidebar_view setNeedsDisplay:YES];
1133 }
1134
1135 - (void)setPause
1136 {
1137     [o_play_btn setImage: o_pause_img];
1138     [o_play_btn setAlternateImage: o_pause_pressed_img];
1139     [o_play_btn setToolTip: _NS("Pause")];
1140     [o_fspanel setPause];
1141 }
1142
1143 - (void)setPlay
1144 {
1145     [o_play_btn setImage: o_play_img];
1146     [o_play_btn setAlternateImage: o_play_pressed_img];
1147     [o_play_btn setToolTip: _NS("Play")];
1148     [o_fspanel setPlay];
1149 }
1150
1151 - (void)drawFancyGradientEffectForTimeSlider
1152 {
1153     if (OSX_LEOPARD)
1154         return;
1155
1156     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1157     CGFloat f_value = [o_time_sld knobPosition];
1158     if (f_value > 7.5)
1159     {
1160         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1161         if (f_value != oldFrame.size.width)
1162         {
1163             [o_time_sld_fancygradient_view setHidden: NO];
1164             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1165             [o_time_sld_fancygradient_view setNeedsDisplay:YES];
1166         }
1167     }
1168     else
1169     {
1170         [o_time_sld_fancygradient_view setHidden: YES];
1171     }
1172     [o_pool release];
1173 }
1174
1175 #pragma mark -
1176 #pragma mark Video Output handling
1177
1178 - (id)videoView
1179 {
1180     vout_thread_t *p_vout = getVout();
1181     if (config_GetInt( VLCIntf, "embedded-video" ))
1182     {
1183         if ([o_video_view window] != self)
1184         {
1185             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1186             [o_video_view setFrame: [o_split_view frame]];
1187             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1188         }
1189         b_nonembedded = NO;
1190     }
1191     else
1192     {
1193         if ([o_video_view superview] != NULL)
1194             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1195         if (o_nonembedded_window)
1196             [o_nonembedded_window release];
1197
1198         o_nonembedded_window = [[VLCWindow alloc] initWithContentRect:[o_video_view frame] styleMask: NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:YES];
1199         [o_nonembedded_window setFrame:[o_video_view frame] display:NO];
1200         [o_nonembedded_window setBackgroundColor: [NSColor blackColor]];
1201         [o_nonembedded_window setMovableByWindowBackground: YES];
1202         [o_nonembedded_window setCanBecomeKeyWindow: YES];
1203         [o_nonembedded_window setHasShadow:YES];
1204         [o_nonembedded_window setContentView: o_video_view];
1205         [o_nonembedded_window setLevel:NSNormalWindowLevel];
1206         [o_nonembedded_window useOptimizedDrawing: YES];
1207         [o_nonembedded_window center];
1208         [o_nonembedded_window makeKeyAndOrderFront:self];
1209         [o_nonembedded_window orderFront:self animate:YES];
1210         [o_nonembedded_window setReleasedWhenClosed:NO];
1211         b_nonembedded = YES;
1212     }
1213
1214     if (p_vout)
1215     {
1216         if( var_GetBool( p_vout, "video-on-top" ) )
1217             [[o_video_view window] setLevel: NSStatusWindowLevel];
1218         else
1219             [[o_video_view window] setLevel: NSNormalWindowLevel];
1220         vlc_object_release( p_vout );
1221     }
1222     return o_video_view;
1223 }
1224
1225 - (void)setVideoplayEnabled
1226 {
1227     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1228
1229     if (!b_videoPlayback)
1230         [o_nonembedded_window orderOut: nil];
1231     if( OSX_LION && b_nativeFullscreenMode )
1232     {
1233         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1234             [o_bottombar_view setHidden: b_videoPlayback];
1235         else
1236             [o_bottombar_view setHidden: NO];
1237         if (!b_videoPlayback)
1238             [o_fspanel setNonActive: nil];
1239     }
1240     if (b_videoPlayback)
1241         [self makeFirstResponder: o_video_view];
1242     else
1243         [self makeFirstResponder: nil];
1244
1245     if (!b_videoPlayback && b_fullscreen && !b_nativeFullscreenMode)
1246         [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1247 }
1248
1249 - (void)resizeWindow
1250 {
1251     if ( b_fullscreen || (OSX_LION && [NSApp presentationOptions] == NSApplicationPresentationFullScreen && b_nativeFullscreenMode) )
1252         return;
1253
1254     NSPoint topleftbase = NSMakePoint(0, [self frame].size.height);
1255     NSPoint topleftscreen = [self convertBaseToScreen: topleftbase];
1256
1257     /* Calculate the window's new size */
1258     float w = [self frame].size.width  - [o_video_view frame].size.width
1259         + nativeVideoSize.width;
1260     float h = [self frame].size.height - [o_video_view frame].size.height
1261         + nativeVideoSize.height;
1262
1263     if (b_dark_interface)
1264         h += [o_titlebar_view frame].size.height;
1265
1266     NSRect new_frame = NSMakeRect(topleftscreen.x, topleftscreen.y - h, w, h);
1267
1268     [[self animator] setFrame:new_frame display:YES];
1269 }
1270
1271 - (void)setNativeVideoSize:(NSSize)size
1272 {
1273     if (size.width != nativeVideoSize.width || size.height != nativeVideoSize.height )
1274     {
1275         nativeVideoSize = size;
1276         [self resizeWindow];
1277     }
1278 }
1279
1280 //  Called automatically if window's acceptsMouseMovedEvents property is true
1281 - (void)mouseMoved:(NSEvent *)theEvent
1282 {
1283     if (b_fullscreen)
1284         [self recreateHideMouseTimer];
1285
1286     [super mouseMoved: theEvent];
1287 }
1288
1289 - (void)recreateHideMouseTimer
1290 {
1291     if (t_hide_mouse_timer != nil) {
1292         [t_hide_mouse_timer invalidate];
1293         [t_hide_mouse_timer release];
1294     }
1295
1296     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
1297                                                           target:self
1298                                                         selector:@selector(hideMouseCursor:)
1299                                                         userInfo:nil
1300                                                          repeats:NO];
1301     [t_hide_mouse_timer retain];
1302 }
1303
1304 //  NSTimer selectors require this function signature as per Apple's docs
1305 - (void)hideMouseCursor:(NSTimer *)timer
1306 {
1307     [NSCursor setHiddenUntilMouseMoves: YES];
1308 }
1309
1310 #pragma mark -
1311 #pragma mark Fullscreen support
1312 - (void)showFullscreenController
1313 {
1314      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1315         [o_fspanel fadeIn];
1316 }
1317
1318 - (BOOL)isFullscreen
1319 {
1320     return b_fullscreen;
1321 }
1322
1323 - (void)lockFullscreenAnimation
1324 {
1325     [o_animation_lock lock];
1326 }
1327
1328 - (void)unlockFullscreenAnimation
1329 {
1330     [o_animation_lock unlock];
1331 }
1332
1333 - (void)enterFullscreen
1334 {
1335     NSMutableDictionary *dict1, *dict2;
1336     NSScreen *screen;
1337     NSRect screen_rect;
1338     NSRect rect;
1339     vout_thread_t *p_vout = getVout();
1340     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1341     id o_videoWindow = b_nonembedded ? o_nonembedded_window : self;
1342
1343     if( p_vout )
1344         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)config_GetInt( VLCIntf, "macosx-vdev" )];
1345
1346     [self lockFullscreenAnimation];
1347
1348     if (!screen)
1349     {
1350         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1351         screen = [o_videoWindow screen];
1352     }
1353     if (!screen)
1354     {
1355         msg_Dbg( VLCIntf, "Using deepest screen" );
1356         screen = [NSScreen deepestScreen];
1357     }
1358
1359     if( p_vout )
1360         vlc_object_release( p_vout );
1361
1362     screen_rect = [screen frame];
1363
1364     [o_fullscreen_btn setState: YES];
1365
1366     [self recreateHideMouseTimer];
1367
1368     if( blackout_other_displays )
1369         [screen blackoutOtherScreens];
1370
1371     /* Make sure we don't see the window flashes in float-on-top mode */
1372     i_originalLevel = [o_videoWindow level];
1373     [o_videoWindow setLevel:NSNormalWindowLevel];
1374
1375     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1376     if (!o_fullscreen_window)
1377     {
1378         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1379
1380         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1381         rect.origin.x += [o_videoWindow frame].origin.x;
1382         rect.origin.y += [o_videoWindow frame].origin.y;
1383         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1384         [o_fullscreen_window setFullscreen: YES];
1385         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1386         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1387
1388         if (![o_videoWindow isVisible] || [o_videoWindow alphaValue] == 0.0)
1389         {
1390             /* We don't animate if we are not visible, instead we
1391              * simply fade the display */
1392             CGDisplayFadeReservationToken token;
1393
1394             if( blackout_other_displays )
1395             {
1396                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1397                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1398             }
1399
1400             if ([screen isMainScreen])
1401             {
1402                 if (OSX_LEOPARD)
1403                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1404                 else
1405                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1406             }
1407
1408             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1409             [o_temp_view setFrame:[o_video_view frame]];
1410             [o_fullscreen_window setContentView:o_video_view];
1411
1412             [o_fullscreen_window makeKeyAndOrderFront:self];
1413             [o_fullscreen_window orderFront:self animate:YES];
1414
1415             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1416             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1417
1418             if( blackout_other_displays )
1419             {
1420                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1421                 CGReleaseDisplayFadeReservation( token );
1422             }
1423
1424             /* Will release the lock */
1425             [self hasBecomeFullscreen];
1426
1427             return;
1428         }
1429
1430         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1431         NSDisableScreenUpdates();
1432         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1433         [o_temp_view setFrame:[o_video_view frame]];
1434         [o_fullscreen_window setContentView:o_video_view];
1435         [o_fullscreen_window makeKeyAndOrderFront:self];
1436         NSEnableScreenUpdates();
1437     }
1438
1439     /* We are in fullscreen (and no animation is running) */
1440     if (b_fullscreen)
1441     {
1442         /* Make sure we are hidden */
1443         if( b_nonembedded )
1444             [o_nonembedded_window orderOut: self];
1445         else
1446             [super orderOut: self];
1447         
1448         [self unlockFullscreenAnimation];
1449         return;
1450     }
1451
1452     if (o_fullscreen_anim1)
1453     {
1454         [o_fullscreen_anim1 stopAnimation];
1455         [o_fullscreen_anim1 release];
1456     }
1457     if (o_fullscreen_anim2)
1458     {
1459         [o_fullscreen_anim2 stopAnimation];
1460         [o_fullscreen_anim2 release];
1461     }
1462
1463     if ([screen isMainScreen])
1464     {
1465         if (OSX_LEOPARD)
1466             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1467         else
1468             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1469     }
1470
1471     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1472     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1473
1474     [dict1 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1475     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1476
1477     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1478     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1479     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1480
1481     /* Strategy with NSAnimation allocation:
1482      - Keep at most 2 animation at a time
1483      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1484      */
1485     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1486     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1487
1488     [dict1 release];
1489     [dict2 release];
1490
1491     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1492     [o_fullscreen_anim1 setDuration: 0.3];
1493     [o_fullscreen_anim1 setFrameRate: 30];
1494     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1495     [o_fullscreen_anim2 setDuration: 0.2];
1496     [o_fullscreen_anim2 setFrameRate: 30];
1497
1498     [o_fullscreen_anim2 setDelegate: self];
1499     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1500
1501     [o_fullscreen_anim1 startAnimation];
1502     /* fullscreenAnimation will be unlocked when animation ends */
1503 }
1504
1505 - (void)hasBecomeFullscreen
1506 {
1507     [o_fullscreen_window makeFirstResponder: o_video_view];
1508
1509     [o_fullscreen_window makeKeyWindow];
1510     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1511
1512     /* tell the fspanel to move itself to front next time it's triggered */
1513     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1514     [o_fspanel setActive: nil];
1515
1516     if( !b_nonembedded && [self isVisible] )
1517         [super orderOut: self];
1518
1519     if( b_nonembedded && [o_nonembedded_window isVisible] )
1520         [o_nonembedded_window orderOut: self];
1521
1522     [o_fspanel setActive: nil];
1523
1524     b_fullscreen = YES;
1525     [self unlockFullscreenAnimation];
1526 }
1527
1528 - (void)leaveFullscreen
1529 {
1530     [self leaveFullscreenAndFadeOut: NO];
1531 }
1532
1533 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1534 {
1535     NSMutableDictionary *dict1, *dict2;
1536     NSRect frame;
1537     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1538
1539     [self lockFullscreenAnimation];
1540
1541     b_fullscreen = NO;
1542     [o_fullscreen_btn setState: NO];
1543
1544     /* We always try to do so */
1545     [NSScreen unblackoutScreens];
1546
1547     vout_thread_t *p_vout = getVout();
1548     if (p_vout)
1549     {
1550         if( var_GetBool( p_vout, "video-on-top" ) )
1551             [[o_video_view window] setLevel: NSStatusWindowLevel];
1552         else
1553             [[o_video_view window] setLevel: NSNormalWindowLevel];
1554         vlc_object_release( p_vout );
1555     }
1556     [[o_video_view window] makeKeyAndOrderFront: nil];
1557
1558     /* Don't do anything if o_fullscreen_window is already closed */
1559     if (!o_fullscreen_window)
1560     {
1561         [self unlockFullscreenAnimation];
1562         return;
1563     }
1564
1565     if (fadeout)
1566     {
1567         /* We don't animate if we are not visible, instead we
1568          * simply fade the display */
1569         CGDisplayFadeReservationToken token;
1570
1571         if( blackout_other_displays )
1572         {
1573             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1574             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1575         }
1576
1577         [o_fspanel setNonActive: nil];
1578         if (OSX_LEOPARD)
1579             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1580         else
1581             [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1582
1583         /* Will release the lock */
1584         [self hasEndedFullscreen];
1585
1586         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1587          * here */
1588         b_window_is_invisible = YES;
1589
1590         if( blackout_other_displays )
1591         {
1592             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1593             CGReleaseDisplayFadeReservation( token );
1594         }
1595
1596         return;
1597     }
1598
1599     id o_videoWindow = b_nonembedded ? o_nonembedded_window : self;
1600
1601     [o_videoWindow setAlphaValue: 0.0];
1602     [o_videoWindow orderFront: self];
1603     [[o_video_view window] orderFront: self];
1604
1605     [o_fspanel setNonActive: nil];
1606     if (OSX_LEOPARD)
1607         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1608     else
1609         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1610
1611     if (o_fullscreen_anim1)
1612     {
1613         [o_fullscreen_anim1 stopAnimation];
1614         [o_fullscreen_anim1 release];
1615     }
1616     if (o_fullscreen_anim2)
1617     {
1618         [o_fullscreen_anim2 stopAnimation];
1619         [o_fullscreen_anim2 release];
1620     }
1621
1622     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1623     frame.origin.x += [o_videoWindow frame].origin.x;
1624     frame.origin.y += [o_videoWindow frame].origin.y;
1625
1626     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1627     [dict2 setObject:o_videoWindow forKey:NSViewAnimationTargetKey];
1628     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1629
1630     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1631     [dict2 release];
1632
1633     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1634     [o_fullscreen_anim2 setDuration: 0.3];
1635     [o_fullscreen_anim2 setFrameRate: 30];
1636
1637     [o_fullscreen_anim2 setDelegate: self];
1638
1639     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1640
1641     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1642     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1643     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1644
1645     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1646     [dict1 release];
1647
1648     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1649     [o_fullscreen_anim1 setDuration: 0.2];
1650     [o_fullscreen_anim1 setFrameRate: 30];
1651     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1652
1653     /* Make sure o_fullscreen_window is the frontmost window */
1654     [o_fullscreen_window orderFront: self];
1655
1656     [o_fullscreen_anim1 startAnimation];
1657     /* fullscreenAnimation will be unlocked when animation ends */
1658 }
1659
1660 - (void)hasEndedFullscreen
1661 {
1662     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1663     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1664     NSDisableScreenUpdates();
1665     [o_video_view retain];
1666     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1667     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1668     [o_video_view release];
1669     [o_video_view setFrame:[o_temp_view frame]];
1670     [[o_video_view window] makeFirstResponder: o_video_view];
1671     if( [[o_video_view window] isVisible] )
1672     {
1673         if( !b_nonembedded )
1674             [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1675         else
1676             [[o_video_view window] makeKeyAndOrderFront: self];
1677     }
1678     [o_fullscreen_window orderOut: self];
1679     NSEnableScreenUpdates();
1680
1681     [o_fullscreen_window release];
1682     o_fullscreen_window = nil;
1683     [[o_video_view window] setLevel:i_originalLevel];
1684
1685     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
1686     if( ![[VLCMain sharedInstance] activeVideoPlayback] && b_nonembedded )
1687         [o_nonembedded_window orderOut: self];
1688
1689     [self unlockFullscreenAnimation];
1690 }
1691
1692 - (void)animationDidEnd:(NSAnimation*)animation
1693 {
1694     NSArray *viewAnimations;
1695     if( o_makekey_anim == animation )
1696     {
1697         [o_makekey_anim release];
1698         return;
1699     }
1700     if ([animation currentValue] < 1.0)
1701         return;
1702
1703     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1704     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1705     if ([viewAnimations count] >=1 &&
1706         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
1707     {
1708         /* Fullscreen ended */
1709         [self hasEndedFullscreen];
1710     }
1711     else
1712     {
1713         /* Fullscreen started */
1714         [self hasBecomeFullscreen];
1715     }
1716 }
1717
1718 - (void)orderOut: (id)sender
1719 {
1720     /* Make sure we leave fullscreen */
1721     if (!(OSX_LION || !b_nativeFullscreenMode))
1722         [self leaveFullscreenAndFadeOut: YES];
1723
1724     [super orderOut: sender];
1725 }
1726
1727 - (void)makeKeyAndOrderFront: (id)sender
1728 {
1729     /* Hack
1730      * when we exit fullscreen and fade out, we may endup in
1731      * having a window that is faded. We can't have it fade in unless we
1732      * animate again. */
1733
1734     if(!b_window_is_invisible)
1735     {
1736         /* Make sure we don't do it too much */
1737         [super makeKeyAndOrderFront: sender];
1738         return;
1739     }
1740
1741     [super setAlphaValue:0.0f];
1742     [super makeKeyAndOrderFront: sender];
1743
1744     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1745     [dict setObject:self forKey:NSViewAnimationTargetKey];
1746     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1747
1748     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1749     [dict release];
1750
1751     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1752     [o_makekey_anim setDuration: 0.1];
1753     [o_makekey_anim setFrameRate: 30];
1754     [o_makekey_anim setDelegate: self];
1755
1756     [o_makekey_anim startAnimation];
1757     b_window_is_invisible = NO;
1758
1759     /* fullscreenAnimation will be unlocked when animation ends */
1760 }
1761
1762 /* Make sure setFrame gets executed on main thread especially if we are animating.
1763  * (Thus we won't block the video output thread) */
1764 - (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate
1765 {
1766     struct { NSRect frame; BOOL display; BOOL animate;} args;
1767     NSData *packedargs;
1768
1769     args.frame = frame;
1770     args.display = display;
1771     args.animate = animate;
1772
1773     packedargs = [NSData dataWithBytes:&args length:sizeof(args)];
1774
1775     [self performSelectorOnMainThread:@selector(setFrameOnMainThread:)
1776                            withObject: packedargs waitUntilDone: YES];
1777 }
1778
1779 - (void)setFrameOnMainThread:(NSData*)packedargs
1780 {
1781     struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes];
1782
1783     if( args->animate )
1784     {
1785         /* Make sure we don't block too long and set up a non blocking animation */
1786         NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
1787                                self, NSViewAnimationTargetKey,
1788                                [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey,
1789                                [NSValue valueWithRect:args->frame], NSViewAnimationEndFrameKey, nil];
1790
1791         NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1792
1793         [anim setAnimationBlockingMode: NSAnimationNonblocking];
1794         [anim setDuration: 0.4];
1795         [anim setFrameRate: 30];
1796         [anim startAnimation];
1797
1798         [anim release];
1799     }
1800     else {
1801         [super setFrame:args->frame display:args->display animate:args->animate];
1802     }
1803 }
1804
1805 #pragma mark -
1806 #pragma mark Lion's native fullscreen handling
1807 - (void)windowWillEnterFullScreen:(NSNotification *)notification
1808 {
1809     [o_video_view setFrame: [[self contentView] frame]];
1810     b_fullscreen = YES;
1811     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
1812     [o_fspanel setActive: nil];
1813
1814     [self recreateHideMouseTimer];
1815     i_originalLevel = [self level];
1816     [self setLevel:NSNormalWindowLevel];
1817
1818     if (b_dark_interface)
1819     {
1820         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
1821
1822         NSRect winrect;
1823         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1824         winrect = [self frame];
1825
1826         winrect.size.height = winrect.size.height - f_titleBarHeight;
1827         [self setFrame: winrect display:NO animate:NO];
1828         winrect = [o_split_view frame];
1829         winrect.size.height = winrect.size.height + f_titleBarHeight;
1830         [o_split_view setFrame: winrect];
1831     }
1832
1833     if ([[VLCMain sharedInstance] activeVideoPlayback])
1834         [o_bottombar_view setHidden: YES];
1835 }
1836
1837 - (void)windowWillExitFullScreen:(NSNotification *)notification
1838 {
1839     [o_video_view setFrame: [o_split_view frame]];
1840     [NSCursor setHiddenUntilMouseMoves: NO];
1841     [o_fspanel setNonActive: nil];
1842     [self setLevel:i_originalLevel];
1843     b_fullscreen = NO;
1844
1845     if (b_dark_interface)
1846     {
1847         NSRect winrect;
1848         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1849         winrect = [self frame];
1850         
1851         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
1852                                               winrect.size.width, f_titleBarHeight )];
1853         [[self contentView] addSubview: o_titlebar_view];
1854         
1855         winrect.size.height = winrect.size.height + f_titleBarHeight;
1856         [self setFrame: winrect display:NO animate:NO];
1857         winrect = [o_split_view frame];
1858         winrect.size.height = winrect.size.height - f_titleBarHeight;
1859         [o_split_view setFrame: winrect];
1860         [o_video_view setFrame: winrect];
1861     }
1862
1863     if ([[VLCMain sharedInstance] activeVideoPlayback])
1864         [o_bottombar_view setHidden: NO];
1865 }
1866
1867 #pragma mark -
1868 #pragma mark split view delegate
1869 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
1870 {
1871     if (dividerIndex == 0)
1872         return 200.0;
1873     else
1874         return proposedMin;
1875 }
1876
1877 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
1878 {
1879     if (dividerIndex == 0)
1880         return ([self frame].size.width - 300.0);
1881     else
1882         return proposedMax;
1883 }
1884
1885 #pragma mark -
1886 #pragma mark Side Bar Data handling
1887 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1888 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
1889 {
1890         //Works the same way as the NSOutlineView data source: `nil` means a parent item
1891         if(item==nil) {
1892                 return [o_sidebaritems count];
1893         }
1894         else {
1895                 return [[item children] count];
1896         }
1897 }
1898
1899
1900 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1901 {
1902     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1903         if(item==nil) {
1904                 return [o_sidebaritems objectAtIndex:index];
1905         }
1906         else {
1907                 return [[item children] objectAtIndex:index];
1908         }
1909 }
1910
1911
1912 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1913 {
1914         return [item title];
1915 }
1916
1917 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1918 {
1919         [item setTitle:object];
1920 }
1921
1922 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1923 {
1924         return [item hasChildren];
1925 }
1926
1927
1928 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1929 {
1930     if ([[item identifier] isEqualToString: @"playlist"] || [[item identifier] isEqualToString: @"medialibrary"])
1931         return YES;
1932
1933         return [item hasBadge];
1934 }
1935
1936
1937 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1938 {
1939     playlist_t * p_playlist = pl_Get( VLCIntf );
1940     NSInteger i_playlist_size;
1941
1942     if ([[item identifier] isEqualToString: @"playlist"])
1943     {
1944         PL_LOCK;
1945         i_playlist_size = p_playlist->p_local_category->i_children;
1946         PL_UNLOCK;
1947
1948         return i_playlist_size;
1949     }
1950     if ([[item identifier] isEqualToString: @"medialibrary"])
1951     {
1952         PL_LOCK;
1953         i_playlist_size = p_playlist->p_ml_category->i_children;
1954         PL_UNLOCK;
1955
1956         return i_playlist_size;
1957     }
1958         return [item badgeValue];
1959 }
1960
1961
1962 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1963 {
1964         return [item hasIcon];
1965 }
1966
1967
1968 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1969 {
1970         return [item icon];
1971 }
1972
1973 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1974 {
1975         if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1976                 if (item != nil)
1977         {
1978             NSMenu * m;
1979             if ([item sdtype] > 0)
1980             {
1981                 m = [[NSMenu alloc] init];
1982                 playlist_t * p_playlist = pl_Get( VLCIntf );
1983                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
1984                 if (!sd_loaded)
1985                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1986                 else
1987                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1988                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1989             }
1990             return [m autorelease];
1991         }
1992         }
1993         return nil;
1994 }
1995
1996 - (IBAction)sdmenuhandler:(id)sender
1997 {
1998     NSString * identifier = [sender representedObject];
1999     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
2000     {
2001         playlist_t * p_playlist = pl_Get( VLCIntf );
2002         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
2003
2004         if (!sd_loaded)
2005             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
2006         else
2007             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
2008     }
2009 }
2010
2011 #pragma mark -
2012 #pragma mark Side Bar Delegate Methods
2013 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
2014 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
2015 {
2016     return NO;
2017 }
2018
2019 - (void)sourceListSelectionDidChange:(NSNotification *)notification
2020 {
2021     playlist_t * p_playlist = pl_Get( VLCIntf );
2022         NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
2023     id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
2024
2025         //Set the label text to represent the new selection
2026     if ([item sdtype] > -1)
2027     {
2028         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
2029         if (!sd_loaded)
2030         {
2031             playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
2032         }
2033     }
2034
2035     [o_chosen_category_lbl setStringValue:[item title]];
2036
2037     if ([[item identifier] isEqualToString:@"playlist"])
2038     {
2039         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_local_category];
2040     }
2041     else if([[item identifier] isEqualToString:@"medialibrary"])
2042     {
2043         [[[VLCMain sharedInstance] playlist] setPlaylistRoot:p_playlist->p_ml_category];
2044     }
2045     else
2046     {
2047         playlist_item_t * pl_item;
2048         PL_LOCK;
2049         pl_item = playlist_ChildSearchName( p_playlist->p_root, [[item untranslatedTitle] UTF8String] );
2050         PL_UNLOCK;
2051         [[[VLCMain sharedInstance] playlist] setPlaylistRoot: pl_item];
2052     }
2053
2054     PL_LOCK;
2055     if ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] != p_playlist->p_local_category || p_playlist->p_local_category->i_children > 0)
2056         [self hideDropZone];
2057     else
2058         [self showDropZone];
2059     PL_UNLOCK;
2060 }
2061
2062 @end