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