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