]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: make sure that the non-native fullscreen mode is correctly left when a clip...
[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     [o_sidebaritems release];
116     [super dealloc];
117 }
118
119 - (void)awakeFromNib
120 {
121     /* setup the styled interface */
122 #ifndef MAC_OS_X_VERSION_10_7
123     b_nativeFullscreenMode = NO;
124 #else
125     b_nativeFullscreenMode = config_GetInt( VLCIntf, "macosx-nativefullscreenmode" );
126 #endif
127     i_lastShownVolume = -1;
128     t_hide_mouse_timer = nil;
129
130     [o_play_btn setToolTip: _NS("Play/Pause")];
131     [o_bwd_btn setToolTip: _NS("Backward")];
132     [o_fwd_btn setToolTip: _NS("Forward")];
133     [o_stop_btn setToolTip: _NS("Stop")];
134     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
135     [o_repeat_btn setToolTip: _NS("Repeat")];
136     [o_shuffle_btn setToolTip: _NS("Shuffle")];
137     [o_effects_btn setToolTip: _NS("Effects")];
138     [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
139     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
140     [o_volume_sld setToolTip: _NS("Volume")];
141     [o_volume_down_btn setToolTip: _NS("Mute")];
142     [o_volume_up_btn setToolTip: _NS("Full Volume")];
143     [o_time_sld setToolTip: _NS("Position")];
144     [o_dropzone_btn setTitle: _NS("Open media...")];
145     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
146
147     if (!b_dark_interface) {
148         [o_bottombar_view setImage: [NSImage imageNamed:@"bottom-background"]];
149         [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
150         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
151         o_play_img = [[NSImage imageNamed:@"play"] retain];
152         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
153         o_pause_img = [[NSImage imageNamed:@"pause"] retain];
154         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
155         [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
156         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
157         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
158         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
159         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist"]];
160         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed"]];
161         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
162         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
163         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
164         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
165         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
166         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
167         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
168         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
169         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
170         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
171         [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"]];
172         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
173         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
174         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
175         if (OSX_LION && b_nativeFullscreenMode)
176         {
177             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button"]];
178             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue"]];
179         }
180         else
181         {
182             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
183             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
184         }
185         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
186         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
187         [o_time_sld_fancygradient_view setImagesLeft:[NSImage imageNamed:@"progression-fill-left"] middle:[NSImage imageNamed:@"progression-fill-middle"] right:[NSImage imageNamed:@"progression-fill-right"]];
188     }
189     else
190     {
191         [o_bottombar_view setImage: [NSImage imageNamed:@"bottom-background_dark"]];
192         [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
193         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
194         o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
195         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
196         o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
197         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
198         [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
199         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
200         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
201         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
202         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
203         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
204         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
205         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
206         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
207         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
208         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
209         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
210         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
211         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
212         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
213         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
214         [o_time_fld setTextColor: [NSColor colorWithCalibratedRed:229.0 green:229.0 blue:229.0 alpha:100.0]];
215         [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"]];
216         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
217         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
218         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
219         if (OSX_LION && b_nativeFullscreenMode)
220         {
221             [o_effects_btn setImage: [NSImage imageNamed:@"effects-one-button_dark"]];
222             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-one-button-blue_dark"]];
223         }
224         else
225         {
226             [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
227             [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];            
228         }
229         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
230         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
231         [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"]];
232     }
233     [o_repeat_btn setImage: o_repeat_img];
234     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
235     [o_shuffle_btn setImage: o_shuffle_img];
236     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
237     [o_play_btn setImage: o_play_img];
238     [o_play_btn setAlternateImage: o_play_pressed_img];
239     BOOL b_mute = ![[VLCCoreInteraction sharedInstance] isMuted];
240     [o_volume_sld setEnabled: b_mute];
241     [o_volume_up_btn setEnabled: b_mute];
242
243     /* interface builder action */
244     [self setDelegate: self];
245     [self setExcludedFromWindowsMenu: YES];
246     [self setAcceptsMouseMovedEvents: YES];
247     // Set that here as IB seems to be buggy
248     if (b_dark_interface)
249         [self setContentMinSize:NSMakeSize(500., (288. + [o_titlebar_view frame].size.height))];
250     else
251         [self setContentMinSize:NSMakeSize(500., 288.)];
252     [self setTitle: _NS("VLC media player")];
253     [o_playlist_btn setEnabled:NO];
254     o_temp_view = [[NSView alloc] init];
255     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
256     [o_dropzone_view setFrame: [o_playlist_table frame]];
257     [o_left_split_view setFrame: [o_sidebar_view frame]];
258     if (OSX_LION && b_nativeFullscreenMode)
259     {
260         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
261         NSRect frame;
262         float f_width = [o_fullscreen_btn frame].size.width;
263
264         #define moveItem( item ) \
265         frame = [item frame]; \
266         frame.origin.x = f_width + frame.origin.x; \
267         [item setFrame: frame]
268
269         moveItem( o_effects_btn );
270         moveItem( o_volume_up_btn );
271         moveItem( o_volume_sld );
272         moveItem( o_volume_track_view );
273         moveItem( o_volume_down_btn );
274         moveItem( o_time_fld );
275         #undef moveItem
276
277         #define enlargeItem( item ) \
278         frame = [item frame]; \
279         frame.size.width = f_width + frame.size.width; \
280         [item setFrame: frame]
281
282         enlargeItem( o_time_sld );
283         enlargeItem( o_progress_bar );
284         enlargeItem( o_time_sld_background );
285         enlargeItem( o_time_sld_fancygradient_view );
286         #undef enlargeItem
287
288         [o_fullscreen_btn removeFromSuperviewWithoutNeedingDisplay];
289     }
290     else
291         [o_titlebar_view setFullscreenButtonHidden: YES];
292
293     if (OSX_LION)
294     {
295         /* the default small size of the search field is slightly different on Lion, let's work-around that */
296         NSRect frame;
297         frame = [o_search_fld frame];
298         frame.origin.y = frame.origin.y + 2.0;
299         frame.size.height = frame.size.height - 1.0;
300         [o_search_fld setFrame: frame];
301     }
302
303     /* create the sidebar */
304     o_sidebaritems = [[NSMutableArray alloc] init];
305     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
306     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
307     [playlistItem setIcon: [NSImage imageNamed:@"sidebar-playlist"]];
308     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
309     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
310     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
311     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
312
313     /* SD subnodes, inspired by the Qt4 intf */
314     char **ppsz_longnames;
315     int *p_categories;
316     char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
317     if (!ppsz_names)
318         msg_Err( VLCIntf, "no sd item found" ); //TODO
319     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
320     int *p_category = p_categories;
321     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
322     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
323     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
324     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
325     NSString *o_identifier;
326     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
327     {
328         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
329         switch (*p_category) {
330             case SD_CAT_INTERNET:
331                 {
332                     [internetItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
333                     if (!strncmp( *ppsz_name, "podcast", 7 ))
334                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-podcast"]];
335                     else
336                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
337                     [[internetItems lastObject] setSdtype: SD_CAT_INTERNET];
338                 }
339                 break;
340             case SD_CAT_DEVICES:
341                 {
342                     [devicesItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
343                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
344                     [[devicesItems lastObject] setSdtype: SD_CAT_DEVICES];
345                 }
346                 break;
347             case SD_CAT_LAN:
348                 {
349                     [lanItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
350                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-local"]];
351                     [[lanItems lastObject] setSdtype: SD_CAT_LAN];
352                 }
353                 break;
354             case SD_CAT_MYCOMPUTER:
355                 {
356                     [mycompItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
357                     if (!strncmp( *ppsz_name, "video_dir", 9 ))
358                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-movie"]];
359                     else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
360                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-music"]];
361                     else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
362                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"sidebar-pictures"]];
363                     else
364                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
365                     [[mycompItems lastObject] setSdtype: SD_CAT_MYCOMPUTER];
366                 }
367                 break;
368             default:
369                 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
370                 break;
371         }
372
373         free( *ppsz_name );
374         free( *ppsz_longname );
375     }
376     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
377     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
378     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
379     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
380     [mycompItems release];
381     [devicesItems release];
382     [lanItems release];
383     [internetItems release];
384     free( ppsz_names );
385     free( ppsz_longnames );
386     free( p_categories );
387
388     [libraryItem setChildren: [NSArray arrayWithObject: playlistItem]];
389     [o_sidebaritems addObject: libraryItem];
390     if ([mycompItem hasChildren])
391         [o_sidebaritems addObject: mycompItem];
392     if ([devicesItem hasChildren])
393         [o_sidebaritems addObject: devicesItem];
394     if ([lanItem hasChildren])
395         [o_sidebaritems addObject: lanItem];
396     if ([internetItem hasChildren])
397         [o_sidebaritems addObject: internetItem];
398
399     [o_sidebar_view reloadData];
400     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:YES];
401     NSUInteger i_sidebaritem_count = [o_sidebaritems count];
402     for (NSUInteger x = 0; x < i_sidebaritem_count; x++)
403         [o_sidebar_view expandItem: [o_sidebaritems objectAtIndex: x] expandChildren: YES];
404
405     if( b_dark_interface )
406     {
407         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidResizeNotification object: nil];
408         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(windowResizedOrMoved:) name: NSWindowDidMoveNotification object: nil];
409
410         [self setBackgroundColor: [NSColor clearColor]];
411         [self setOpaque: NO];
412         [self setHasShadow:YES];
413
414         NSRect winrect;
415         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
416         winrect = [self frame];
417
418         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
419                                               winrect.size.width, f_titleBarHeight )];
420         [[self contentView] addSubview: o_titlebar_view];
421
422         winrect.size.height = winrect.size.height + f_titleBarHeight;
423         [self setFrame: winrect display:NO animate:NO];
424         winrect = [o_split_view frame];
425         winrect.size.height = winrect.size.height - f_titleBarHeight;
426         [o_split_view setFrame: winrect];
427         [o_video_view setFrame: winrect];
428
429         o_color_backdrop = [[VLCColorView alloc] initWithFrame: [o_split_view frame]];
430         [[self contentView] addSubview: o_color_backdrop positioned: NSWindowBelow relativeTo: o_split_view];
431         [o_color_backdrop setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
432
433         previousSavedFrame = winrect;
434
435         [self display];
436     }
437     else
438     {
439         NSRect frame;
440         frame = [o_time_sld_fancygradient_view frame];
441         frame.size.height = frame.size.height - 1;
442         frame.origin.y = frame.origin.y + 1;
443         [o_time_sld_fancygradient_view setFrame: frame];
444
445         [o_video_view setFrame: [o_split_view frame]];
446         [o_playlist_table setBorderType: NSNoBorder];
447         [o_sidebar_scrollview setBorderType: NSNoBorder];
448     }
449
450     if (OSX_LION)
451         [o_resize_view setImage: NULL];
452
453     if ([self styleMask] & NSResizableWindowMask)
454         [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
455
456     if (OSX_LEOPARD)
457         [o_time_sld_fancygradient_view removeFromSuperviewWithoutNeedingDisplay];
458
459     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillClose:) name: NSWindowWillCloseNotification object: nil];
460     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someWindowWillMiniaturize:) name: NSWindowWillMiniaturizeNotification object:nil];
461     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(applicationWillTerminate:) name: NSApplicationWillTerminateNotification 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 - (void)applicationWillTerminate:(NSNotification *)notification
898 {
899     config_PutInt( VLCIntf->p_libvlc, "volume", i_lastShownVolume );
900     [self saveFrameUsingName: [self frameAutosaveName]];
901 }
902
903 - (void)someWindowWillClose:(NSNotification *)notification
904 {
905     if([notification object] == o_nonembedded_window || [notification object] == self)
906         [[VLCCoreInteraction sharedInstance] stop];
907 }
908
909 - (void)someWindowWillMiniaturize:(NSNotification *)notification
910 {
911     if([notification object] == o_nonembedded_window || [notification object] == self)
912     {
913         if([[VLCMain sharedInstance] activeVideoPlayback])
914             [[VLCCoreInteraction sharedInstance] pause];
915     }
916 }
917
918 #pragma mark -
919 #pragma mark Update interface and respond to foreign events
920 - (void)showDropZone
921 {
922     [o_right_split_view addSubview: o_dropzone_view];
923     [o_dropzone_view setFrame: [o_playlist_table frame]];
924     [[o_playlist_table animator] setHidden:YES];
925 }
926
927 - (void)hideDropZone
928 {
929     [o_dropzone_view removeFromSuperview];
930     [[o_playlist_table animator] setHidden: NO];
931 }
932
933 - (void)updateTimeSlider
934 {
935     input_thread_t * p_input;
936     p_input = pl_CurrentInput( VLCIntf );
937     if( p_input )
938     {
939         vlc_value_t time;
940         NSString * o_time;
941         vlc_value_t pos;
942         char psz_time[MSTRTIME_MAX_SIZE];
943         float f_updated;
944
945         var_Get( p_input, "position", &pos );
946         f_updated = 10000. * pos.f_float;
947         [o_time_sld setFloatValue: f_updated];
948
949         var_Get( p_input, "time", &time );
950
951         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
952         if( [o_time_fld timeRemaining] && dur != -1 )
953         {
954             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
955         }
956         else
957             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
958
959         if (dur == -1) {
960             [o_time_sld setEnabled: NO];
961             [o_time_sld setHidden: YES];
962         } else {
963             [o_time_sld setEnabled: YES];
964             [o_time_sld setHidden: NO];
965         }
966
967         [o_time_fld setStringValue: o_time];
968         [o_time_fld setNeedsDisplay:YES];
969         [o_fspanel setStreamPos: f_updated andTime: o_time];
970         vlc_object_release( p_input );
971     }
972     else
973     {
974         [o_time_sld setFloatValue: 0.0];
975         [o_time_fld setStringValue: @"00:00"];
976         [o_time_sld setEnabled: NO];
977         [o_time_sld setHidden: YES];
978     }
979         
980     [self performSelectorOnMainThread:@selector(drawFancyGradientEffectForTimeSlider) withObject:nil waitUntilDone:NO];
981 }
982
983 - (void)updateVolumeSlider
984 {
985     audio_volume_t i_volume;
986     playlist_t * p_playlist = pl_Get( VLCIntf );
987
988     i_volume = aout_VolumeGet( p_playlist );
989
990     if( i_volume != i_lastShownVolume )
991     {
992         [o_volume_sld setIntValue: i_volume];
993         [o_fspanel setVolumeLevel: i_volume];
994     }
995 }
996
997 - (void)updateName
998 {
999     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1000     input_thread_t * p_input;
1001     p_input = pl_CurrentInput( VLCIntf );
1002     if( p_input )
1003     {
1004         NSString *aString;
1005         char *format = var_InheritString( VLCIntf, "input-title-format" );
1006         char *formated = str_format_meta( p_input, format );
1007         free( format );
1008         aString = [NSString stringWithUTF8String:formated];
1009         free( formated );
1010
1011         char *uri = input_item_GetURI( input_GetItem( p_input ) );
1012
1013         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
1014         if ([o_url isFileURL])
1015             [self setRepresentedURL: o_url];
1016         else
1017             [self setRepresentedURL: nil];
1018         free( uri );
1019
1020         if ([aString isEqualToString:@""])
1021         {
1022             if ([o_url isFileURL])
1023                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
1024             else
1025                 aString = [o_url absoluteString];
1026         }
1027
1028         [self setTitle: aString];
1029         [o_fspanel setStreamTitle: aString];
1030     }
1031     else
1032     {
1033         [self setTitle: _NS("VLC media player")];
1034         [self setRepresentedURL: nil];
1035     }
1036
1037     [o_pool release];
1038 }
1039
1040 - (void)updateWindow
1041 {
1042     bool b_input = false;
1043     bool b_plmul = false;
1044     bool b_control = false;
1045     bool b_seekable = false;
1046     bool b_chapters = false;
1047
1048     playlist_t * p_playlist = pl_Get( VLCIntf );
1049
1050     PL_LOCK;
1051     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
1052     PL_UNLOCK;
1053
1054     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1055
1056     bool b_buffering = NO;
1057
1058     if( ( b_input = ( p_input != NULL ) ) )
1059     {
1060         /* seekable streams */
1061         cachedInputState = input_GetState( p_input );
1062         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
1063             b_buffering = YES;
1064
1065         /* seekable streams */
1066         b_seekable = var_GetBool( p_input, "can-seek" );
1067
1068         /* check whether slow/fast motion is possible */
1069         b_control = var_GetBool( p_input, "can-rate" );
1070
1071         /* chapters & titles */
1072         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
1073
1074         if (cachedInputState == PLAYING_S || b_buffering == YES)
1075             [self makeKeyAndOrderFront: nil];
1076         vlc_object_release( p_input );
1077     }
1078
1079     if( b_buffering )
1080     {
1081         [o_progress_bar startAnimation:self];
1082         [o_progress_bar setIndeterminate:YES];
1083         [o_progress_bar setHidden:NO];
1084     } else {
1085         [o_progress_bar stopAnimation:self];
1086         [o_progress_bar setHidden:YES];
1087     }
1088
1089     [o_stop_btn setEnabled: b_input];
1090     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1091     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
1092     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
1093
1094     [o_time_sld setEnabled: b_seekable];
1095     [self updateTimeSlider];
1096     [o_fspanel setSeekable: b_seekable];
1097
1098     PL_LOCK;
1099     if (p_playlist->items.i_size >= 1)
1100         [self hideDropZone];
1101     else
1102         [self showDropZone];
1103     PL_UNLOCK;
1104     [o_sidebar_view setNeedsDisplay:YES];
1105 }
1106
1107 - (void)setPause
1108 {
1109     [o_play_btn setImage: o_pause_img];
1110     [o_play_btn setAlternateImage: o_pause_pressed_img];
1111     [o_play_btn setToolTip: _NS("Pause")];
1112     [o_fspanel setPause];
1113 }
1114
1115 - (void)setPlay
1116 {
1117     [o_play_btn setImage: o_play_img];
1118     [o_play_btn setAlternateImage: o_play_pressed_img];
1119     [o_play_btn setToolTip: _NS("Play")];
1120     [o_fspanel setPlay];
1121 }
1122
1123 - (void)drawFancyGradientEffectForTimeSlider
1124 {
1125     if (OSX_LEOPARD)
1126         return;
1127
1128     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1129     CGFloat f_value = [o_time_sld knobPosition];
1130     if (f_value > 7.5)
1131     {
1132         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1133         if (f_value != oldFrame.size.width)
1134         {
1135             [o_time_sld_fancygradient_view setHidden: NO];
1136             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1137             [o_time_sld_fancygradient_view setNeedsDisplay:YES];
1138         }
1139     }
1140     else
1141     {
1142         [o_time_sld_fancygradient_view setHidden: YES];
1143     }
1144     [o_pool release];
1145 }
1146
1147 #pragma mark -
1148 #pragma mark Video Output handling
1149
1150 - (id)videoView
1151 {
1152     vout_thread_t *p_vout = getVout();
1153     if (config_GetInt( VLCIntf, "embedded-video" ))
1154     {
1155         if ([o_video_view window] != self)
1156         {
1157             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1158             [o_video_view setFrame: [o_split_view frame]];
1159             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1160         }
1161         b_nonembedded = NO;
1162     }
1163     else
1164     {
1165         if ([o_video_view superview] != NULL)
1166             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1167         if (o_nonembedded_window)
1168             [o_nonembedded_window release];
1169
1170         o_nonembedded_window = [[VLCWindow alloc] initWithContentRect:[o_video_view frame] styleMask: NSTitledWindowMask|NSClosableWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask backing:NSBackingStoreBuffered defer:YES];
1171         [o_nonembedded_window setFrame:[o_video_view frame] display:NO];
1172         [o_nonembedded_window setBackgroundColor: [NSColor blackColor]];
1173         [o_nonembedded_window setMovableByWindowBackground: YES];
1174         [o_nonembedded_window setCanBecomeKeyWindow: YES];
1175         [o_nonembedded_window setHasShadow:YES];
1176         [o_nonembedded_window setContentView: o_video_view];
1177         [o_nonembedded_window setLevel:NSNormalWindowLevel];
1178         [o_nonembedded_window useOptimizedDrawing: YES];
1179         [o_nonembedded_window center];
1180         [o_nonembedded_window makeKeyAndOrderFront:self];
1181         [o_nonembedded_window orderFront:self animate:YES];
1182         [o_nonembedded_window setReleasedWhenClosed:NO];
1183         b_nonembedded = YES;
1184     }
1185
1186     if (p_vout)
1187     {
1188         if( var_GetBool( p_vout, "video-on-top" ) )
1189             [[o_video_view window] setLevel: NSStatusWindowLevel];
1190         else
1191             [[o_video_view window] setLevel: NSNormalWindowLevel];
1192         vlc_object_release( p_vout );
1193     }
1194     return o_video_view;
1195 }
1196
1197 - (void)setVideoplayEnabled
1198 {
1199     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1200
1201     if (!b_nonembedded)
1202         [o_playlist_btn setEnabled: b_videoPlayback];
1203     else
1204     {
1205         [o_playlist_btn setEnabled: NO];
1206         if (!b_videoPlayback)
1207             [o_nonembedded_window orderOut: nil];
1208     }
1209     if( OSX_LION && b_nativeFullscreenMode )
1210     {
1211         if( [NSApp presentationOptions] & NSApplicationPresentationFullScreen )
1212             [o_bottombar_view setHidden: b_videoPlayback];
1213         else
1214             [o_bottombar_view setHidden: NO];
1215         if (!b_videoPlayback)
1216             [o_fspanel setNonActive: nil];
1217     }
1218     if (b_videoPlayback)
1219         [self makeFirstResponder: o_video_view];
1220     else
1221         [self makeFirstResponder: nil];
1222
1223     if (!b_videoPlayback && b_fullscreen && !b_nativeFullscreenMode)
1224         [self leaveFullscreenAndFadeOut: YES];
1225 }
1226
1227 - (void)resizeWindow
1228 {
1229     if ( !b_fullscreen && !(OSX_LION && [NSApp presentationOptions] == NSApplicationPresentationFullScreen && b_nativeFullscreenMode) )
1230     {
1231         NSPoint topleftbase;
1232         NSPoint topleftscreen;
1233         NSRect new_frame;
1234         topleftbase.x = 0;
1235         topleftbase.y = [self frame].size.height;
1236         topleftscreen = [self convertBaseToScreen: topleftbase];
1237
1238         /* Calculate the window's new size */
1239         new_frame.size.width = [self frame].size.width - [o_video_view frame].size.width + nativeVideoSize.width;
1240         if (b_dark_interface)
1241             new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + nativeVideoSize.height + [o_titlebar_view frame].size.height;
1242         else
1243             new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + nativeVideoSize.height;
1244
1245         new_frame.origin.x = topleftscreen.x;
1246         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1247
1248         [[self animator] setFrame:new_frame display:YES];
1249     }
1250 }
1251
1252 - (void)setNativeVideoSize:(NSSize)size
1253 {
1254     if (size.width != nativeVideoSize.width || size.height != nativeVideoSize.height )
1255     {
1256         nativeVideoSize = size;
1257         [self resizeWindow];
1258     }
1259 }
1260
1261 //  Called automatically if window's acceptsMouseMovedEvents property is true
1262 - (void)mouseMoved:(NSEvent *)theEvent
1263 {
1264     if (b_fullscreen)
1265         [self recreateHideMouseTimer];
1266
1267     [super mouseMoved: theEvent];
1268 }
1269
1270 - (void)recreateHideMouseTimer
1271 {
1272     if (t_hide_mouse_timer != nil) {
1273         [t_hide_mouse_timer invalidate];
1274         [t_hide_mouse_timer release];
1275     }
1276
1277     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
1278                                                           target:self
1279                                                         selector:@selector(hideMouseCursor:)
1280                                                         userInfo:nil
1281                                                          repeats:NO];
1282     [t_hide_mouse_timer retain];
1283 }
1284
1285 //  NSTimer selectors require this function signature as per Apple's docs
1286 - (void)hideMouseCursor:(NSTimer *)timer
1287 {
1288     [NSCursor setHiddenUntilMouseMoves: YES];
1289 }
1290
1291 #pragma mark -
1292 #pragma mark Fullscreen support
1293 - (void)showFullscreenController
1294 {
1295      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1296         [o_fspanel fadeIn];
1297 }
1298
1299 - (BOOL)isFullscreen
1300 {
1301     return b_fullscreen;
1302 }
1303
1304 - (void)lockFullscreenAnimation
1305 {
1306     [o_animation_lock lock];
1307 }
1308
1309 - (void)unlockFullscreenAnimation
1310 {
1311     [o_animation_lock unlock];
1312 }
1313
1314 - (void)enterFullscreen
1315 {
1316     NSMutableDictionary *dict1, *dict2;
1317     NSScreen *screen;
1318     NSRect screen_rect;
1319     NSRect rect;
1320     vout_thread_t *p_vout = getVout();
1321     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1322
1323     if( p_vout )
1324         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)config_GetInt( VLCIntf, "macosx-vdev" )];
1325
1326     [self lockFullscreenAnimation];
1327
1328     if (!screen)
1329     {
1330         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1331         screen = [self screen];
1332     }
1333     if (!screen)
1334     {
1335         msg_Dbg( VLCIntf, "Using deepest screen" );
1336         screen = [NSScreen deepestScreen];
1337     }
1338
1339     if( p_vout )
1340         vlc_object_release( p_vout );
1341
1342     screen_rect = [screen frame];
1343
1344     [o_fullscreen_btn setState: YES];
1345
1346     [self recreateHideMouseTimer];
1347
1348     if( blackout_other_displays )
1349         [screen blackoutOtherScreens];
1350
1351     /* Make sure we don't see the window flashes in float-on-top mode */
1352     i_originalLevel = [self level];
1353     [self setLevel:NSNormalWindowLevel];
1354
1355     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1356     if (!o_fullscreen_window)
1357     {
1358         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1359
1360         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1361         rect.origin.x += [self frame].origin.x;
1362         rect.origin.y += [self frame].origin.y;
1363         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1364         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1365         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1366
1367         if (![self isVisible] || [self alphaValue] == 0.0)
1368         {
1369             /* We don't animate if we are not visible, instead we
1370              * simply fade the display */
1371             CGDisplayFadeReservationToken token;
1372
1373             if( blackout_other_displays )
1374             {
1375                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1376                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1377             }
1378
1379             if ([screen isMainScreen])
1380             {
1381                 if (OSX_LEOPARD)
1382                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1383                 else
1384                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1385             }
1386
1387             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1388             [o_temp_view setFrame:[o_video_view frame]];
1389             [o_fullscreen_window setContentView:o_video_view];
1390
1391             [o_fullscreen_window makeKeyAndOrderFront:self];
1392             [o_fullscreen_window orderFront:self animate:YES];
1393
1394             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1395             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1396
1397             if( blackout_other_displays )
1398             {
1399                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1400                 CGReleaseDisplayFadeReservation( token );
1401             }
1402
1403             /* Will release the lock */
1404             [self hasBecomeFullscreen];
1405
1406             return;
1407         }
1408
1409         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1410         NSDisableScreenUpdates();
1411         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1412         [o_temp_view setFrame:[o_video_view frame]];
1413         [o_fullscreen_window setContentView:o_video_view];
1414         [o_fullscreen_window makeKeyAndOrderFront:self];
1415         NSEnableScreenUpdates();
1416     }
1417
1418     /* We are in fullscreen (and no animation is running) */
1419     if (b_fullscreen)
1420     {
1421         /* Make sure we are hidden */
1422         [super orderOut: self];
1423         [self unlockFullscreenAnimation];
1424         return;
1425     }
1426
1427     if (o_fullscreen_anim1)
1428     {
1429         [o_fullscreen_anim1 stopAnimation];
1430         [o_fullscreen_anim1 release];
1431     }
1432     if (o_fullscreen_anim2)
1433     {
1434         [o_fullscreen_anim2 stopAnimation];
1435         [o_fullscreen_anim2 release];
1436     }
1437
1438     if ([screen isMainScreen])
1439     {
1440         if (OSX_LEOPARD)
1441             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1442         else
1443             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1444     }
1445
1446     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1447     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1448
1449     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
1450     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1451
1452     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1453     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1454     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1455
1456     /* Strategy with NSAnimation allocation:
1457      - Keep at most 2 animation at a time
1458      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1459      */
1460     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1461     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1462
1463     [dict1 release];
1464     [dict2 release];
1465
1466     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1467     [o_fullscreen_anim1 setDuration: 0.3];
1468     [o_fullscreen_anim1 setFrameRate: 30];
1469     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1470     [o_fullscreen_anim2 setDuration: 0.2];
1471     [o_fullscreen_anim2 setFrameRate: 30];
1472
1473     [o_fullscreen_anim2 setDelegate: self];
1474     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1475
1476     [o_fullscreen_anim1 startAnimation];
1477     /* fullscreenAnimation will be unlocked when animation ends */
1478 }
1479
1480 - (void)hasBecomeFullscreen
1481 {
1482     [o_fullscreen_window makeFirstResponder: o_video_view];
1483
1484     [o_fullscreen_window makeKeyWindow];
1485     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1486
1487     /* tell the fspanel to move itself to front next time it's triggered */
1488     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1489     [o_fspanel setActive: nil];
1490
1491     if([self isVisible])
1492         [super orderOut: self];
1493
1494     [o_fspanel setActive: nil];
1495
1496     b_fullscreen = YES;
1497     [self unlockFullscreenAnimation];
1498 }
1499
1500 - (void)leaveFullscreen
1501 {
1502     [self leaveFullscreenAndFadeOut: NO];
1503 }
1504
1505 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1506 {
1507     NSMutableDictionary *dict1, *dict2;
1508     NSRect frame;
1509     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1510
1511     [self lockFullscreenAnimation];
1512
1513     b_fullscreen = NO;
1514     [o_fullscreen_btn setState: NO];
1515
1516     /* We always try to do so */
1517     if (!(OSX_LION || !b_nativeFullscreenMode))
1518         [NSScreen unblackoutScreens];
1519     vout_thread_t *p_vout = getVout();
1520     if (p_vout)
1521     {
1522         if( var_GetBool( p_vout, "video-on-top" ) )
1523             [[o_video_view window] setLevel: NSStatusWindowLevel];
1524         else
1525             [[o_video_view window] setLevel: NSNormalWindowLevel];
1526         vlc_object_release( p_vout );
1527     }
1528     [[o_video_view window] makeKeyAndOrderFront: nil];
1529
1530     /* Don't do anything if o_fullscreen_window is already closed */
1531     if (!o_fullscreen_window)
1532     {
1533         [self unlockFullscreenAnimation];
1534         return;
1535     }
1536
1537     if (fadeout)
1538     {
1539         /* We don't animate if we are not visible, instead we
1540          * simply fade the display */
1541         CGDisplayFadeReservationToken token;
1542
1543         if( blackout_other_displays )
1544         {
1545             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1546             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1547         }
1548
1549         [o_fspanel setNonActive: nil];
1550         if (OSX_LEOPARD)
1551             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1552         else
1553             [NSApp setPresentationOptions: NSApplicationPresentationDefault];
1554
1555         /* Will release the lock */
1556         [self hasEndedFullscreen];
1557
1558         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1559          * here */
1560         b_window_is_invisible = YES;
1561
1562         if( blackout_other_displays )
1563         {
1564             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1565             CGReleaseDisplayFadeReservation( token );
1566         }
1567
1568         return;
1569     }
1570
1571     [self setAlphaValue: 0.0];
1572     [self orderFront: self];
1573     [[o_video_view window] orderFront: self];
1574
1575     [o_fspanel setNonActive: nil];
1576     if (OSX_LEOPARD)
1577         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1578     else
1579         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1580
1581     if (o_fullscreen_anim1)
1582     {
1583         [o_fullscreen_anim1 stopAnimation];
1584         [o_fullscreen_anim1 release];
1585     }
1586     if (o_fullscreen_anim2)
1587     {
1588         [o_fullscreen_anim2 stopAnimation];
1589         [o_fullscreen_anim2 release];
1590     }
1591
1592     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1593     frame.origin.x += [self frame].origin.x;
1594     frame.origin.y += [self frame].origin.y;
1595
1596     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1597     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
1598     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1599
1600     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1601     [dict2 release];
1602
1603     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1604     [o_fullscreen_anim2 setDuration: 0.3];
1605     [o_fullscreen_anim2 setFrameRate: 30];
1606
1607     [o_fullscreen_anim2 setDelegate: self];
1608
1609     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1610
1611     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1612     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1613     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1614
1615     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1616     [dict1 release];
1617
1618     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1619     [o_fullscreen_anim1 setDuration: 0.2];
1620     [o_fullscreen_anim1 setFrameRate: 30];
1621     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1622
1623     /* Make sure o_fullscreen_window is the frontmost window */
1624     [o_fullscreen_window orderFront: self];
1625
1626     [o_fullscreen_anim1 startAnimation];
1627     /* fullscreenAnimation will be unlocked when animation ends */
1628 }
1629
1630 - (void)hasEndedFullscreen
1631 {
1632     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1633     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1634     NSDisableScreenUpdates();
1635     [o_video_view retain];
1636     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1637     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1638     [o_video_view release];
1639     [o_video_view setFrame:[o_temp_view frame]];
1640     [self makeFirstResponder: o_video_view];
1641     if ([self isVisible])
1642         [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1643     [o_fullscreen_window orderOut: self];
1644     NSEnableScreenUpdates();
1645
1646     [o_fullscreen_window release];
1647     o_fullscreen_window = nil;
1648     [self setLevel:i_originalLevel];
1649
1650     [self unlockFullscreenAnimation];
1651 }
1652
1653 - (void)animationDidEnd:(NSAnimation*)animation
1654 {
1655     NSArray *viewAnimations;
1656     if( o_makekey_anim == animation )
1657     {
1658         [o_makekey_anim release];
1659         return;
1660     }
1661     if ([animation currentValue] < 1.0)
1662         return;
1663
1664     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1665     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1666     if ([viewAnimations count] >=1 &&
1667         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
1668     {
1669         /* Fullscreen ended */
1670         [self hasEndedFullscreen];
1671     }
1672     else
1673     {
1674         /* Fullscreen started */
1675         [self hasBecomeFullscreen];
1676     }
1677 }
1678
1679 - (void)orderOut: (id)sender
1680 {
1681     /* Make sure we leave fullscreen */
1682     if (!(OSX_LION || !b_nativeFullscreenMode))
1683         [self leaveFullscreenAndFadeOut: YES];
1684
1685     [super orderOut: sender];
1686 }
1687
1688 - (void)makeKeyAndOrderFront: (id)sender
1689 {
1690     /* Hack
1691      * when we exit fullscreen and fade out, we may endup in
1692      * having a window that is faded. We can't have it fade in unless we
1693      * animate again. */
1694
1695     if(!b_window_is_invisible)
1696     {
1697         /* Make sure we don't do it too much */
1698         [super makeKeyAndOrderFront: sender];
1699         return;
1700     }
1701
1702     [super setAlphaValue:0.0f];
1703     [super makeKeyAndOrderFront: sender];
1704
1705     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1706     [dict setObject:self forKey:NSViewAnimationTargetKey];
1707     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1708
1709     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1710     [dict release];
1711
1712     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1713     [o_makekey_anim setDuration: 0.1];
1714     [o_makekey_anim setFrameRate: 30];
1715     [o_makekey_anim setDelegate: self];
1716
1717     [o_makekey_anim startAnimation];
1718     b_window_is_invisible = NO;
1719
1720     /* fullscreenAnimation will be unlocked when animation ends */
1721 }
1722
1723 /* Make sure setFrame gets executed on main thread especially if we are animating.
1724  * (Thus we won't block the video output thread) */
1725 - (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate
1726 {
1727     struct { NSRect frame; BOOL display; BOOL animate;} args;
1728     NSData *packedargs;
1729
1730     args.frame = frame;
1731     args.display = display;
1732     args.animate = animate;
1733
1734     packedargs = [NSData dataWithBytes:&args length:sizeof(args)];
1735
1736     [self performSelectorOnMainThread:@selector(setFrameOnMainThread:)
1737                            withObject: packedargs waitUntilDone: YES];
1738 }
1739
1740 - (void)setFrameOnMainThread:(NSData*)packedargs
1741 {
1742     struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes];
1743
1744     if( args->animate )
1745     {
1746         /* Make sure we don't block too long and set up a non blocking animation */
1747         NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
1748                                self, NSViewAnimationTargetKey,
1749                                [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey,
1750                                [NSValue valueWithRect:args->frame], NSViewAnimationEndFrameKey, nil];
1751
1752         NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1753
1754         [anim setAnimationBlockingMode: NSAnimationNonblocking];
1755         [anim setDuration: 0.4];
1756         [anim setFrameRate: 30];
1757         [anim startAnimation];
1758
1759         [anim release];
1760     }
1761     else {
1762         [super setFrame:args->frame display:args->display animate:args->animate];
1763     }
1764 }
1765
1766 #pragma mark -
1767 #pragma mark Lion's native fullscreen handling
1768 - (void)windowWillEnterFullScreen:(NSNotification *)notification
1769 {
1770     [o_video_view setFrame: [[self contentView] frame]];
1771     b_fullscreen = YES;
1772     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
1773
1774     [self recreateHideMouseTimer];
1775
1776     if (b_dark_interface)
1777     {
1778         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
1779
1780         NSRect winrect;
1781         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1782         winrect = [self frame];
1783
1784         winrect.size.height = winrect.size.height - f_titleBarHeight;
1785         [self setFrame: winrect display:NO animate:NO];
1786         winrect = [o_split_view frame];
1787         winrect.size.height = winrect.size.height + f_titleBarHeight;
1788         [o_split_view setFrame: winrect];
1789     }
1790
1791     if ([[VLCMain sharedInstance] activeVideoPlayback])
1792         [o_bottombar_view setHidden: YES];
1793 }
1794
1795 - (void)windowWillExitFullScreen:(NSNotification *)notification
1796 {
1797     [o_video_view setFrame: [o_split_view frame]];
1798     [NSCursor setHiddenUntilMouseMoves: NO];
1799     [o_fspanel setNonActive: nil];
1800     b_fullscreen = NO;
1801
1802     if (b_dark_interface)
1803     {
1804         NSRect winrect;
1805         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1806         winrect = [self frame];
1807         
1808         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
1809                                               winrect.size.width, f_titleBarHeight )];
1810         [[self contentView] addSubview: o_titlebar_view];
1811         
1812         winrect.size.height = winrect.size.height + f_titleBarHeight;
1813         [self setFrame: winrect display:NO animate:NO];
1814         winrect = [o_split_view frame];
1815         winrect.size.height = winrect.size.height - f_titleBarHeight;
1816         [o_split_view setFrame: winrect];
1817         [o_video_view setFrame: winrect];
1818     }
1819
1820     if ([[VLCMain sharedInstance] activeVideoPlayback])
1821         [o_bottombar_view setHidden: NO];
1822 }
1823
1824 #pragma mark -
1825 #pragma mark split view delegate
1826 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
1827 {
1828     if (dividerIndex == 0)
1829         return 200.0;
1830     else
1831         return proposedMin;
1832 }
1833
1834 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
1835 {
1836     if (dividerIndex == 0)
1837         return ([self frame].size.width - 300.0);
1838     else
1839         return proposedMax;
1840 }
1841
1842 #pragma mark -
1843 #pragma mark Side Bar Data handling
1844 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1845 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
1846 {
1847         //Works the same way as the NSOutlineView data source: `nil` means a parent item
1848         if(item==nil) {
1849                 return [o_sidebaritems count];
1850         }
1851         else {
1852                 return [[item children] count];
1853         }
1854 }
1855
1856
1857 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1858 {
1859     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1860         if(item==nil) {
1861                 return [o_sidebaritems objectAtIndex:index];
1862         }
1863         else {
1864                 return [[item children] objectAtIndex:index];
1865         }
1866 }
1867
1868
1869 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1870 {
1871         return [item title];
1872 }
1873
1874 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1875 {
1876         [item setTitle:object];
1877 }
1878
1879 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1880 {
1881         return [item hasChildren];
1882 }
1883
1884
1885 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1886 {
1887     if ([[item identifier] isEqualToString: @"playlist"])
1888         return YES;
1889
1890         return [item hasBadge];
1891 }
1892
1893
1894 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1895 {
1896     if ([[item identifier] isEqualToString: @"playlist"]) {
1897         playlist_t * p_playlist = pl_Get( VLCIntf );
1898         NSInteger i_playlist_size;
1899
1900         PL_LOCK;
1901         i_playlist_size = p_playlist->items.i_size;
1902         PL_UNLOCK;
1903
1904         return i_playlist_size;
1905     }
1906         return [item badgeValue];
1907 }
1908
1909
1910 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1911 {
1912         return [item hasIcon];
1913 }
1914
1915
1916 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1917 {
1918         return [item icon];
1919 }
1920
1921 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1922 {
1923         if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1924                 if (item != nil)
1925         {
1926             NSMenu * m;
1927             if ([item sdtype] > 0)
1928             {
1929                 m = [[NSMenu alloc] init];
1930                 playlist_t * p_playlist = pl_Get( VLCIntf );
1931                 BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
1932                 if (!sd_loaded)
1933                     [m addItemWithTitle:_NS("Enable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1934                 else
1935                     [m addItemWithTitle:_NS("Disable") action:@selector(sdmenuhandler:) keyEquivalent:@""];
1936                 [[m itemAtIndex:0] setRepresentedObject: [item identifier]];
1937             }
1938             return [m autorelease];
1939         }
1940         }
1941         return nil;
1942 }
1943
1944 - (IBAction)sdmenuhandler:(id)sender
1945 {
1946     NSString * identifier = [sender representedObject];
1947     if ([identifier length] > 0 && ![identifier isEqualToString:@"lua{sd='freebox',longname='Freebox TV'}"])
1948     {
1949         playlist_t * p_playlist = pl_Get( VLCIntf );
1950         BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [identifier UTF8String] );
1951
1952         if (!sd_loaded)
1953             playlist_ServicesDiscoveryAdd( p_playlist, [identifier UTF8String] );
1954         else
1955             playlist_ServicesDiscoveryRemove( p_playlist, [identifier UTF8String] );
1956     }
1957 }
1958
1959 #pragma mark -
1960 #pragma mark Side Bar Delegate Methods
1961 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1962 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1963 {
1964     return NO;
1965 }
1966
1967 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1968 {
1969         NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1970
1971         //Set the label text to represent the new selection
1972     if([selectedIndexes count]==1) {
1973         id item = [o_sidebar_view itemAtRow:[selectedIndexes firstIndex]];
1974         if ([item sdtype] > -1)
1975         {
1976             playlist_t * p_playlist = pl_Get( VLCIntf );
1977             BOOL sd_loaded = playlist_IsServicesDiscoveryLoaded( p_playlist, [[item identifier] UTF8String] );
1978             if (!sd_loaded)
1979             {
1980                 playlist_ServicesDiscoveryAdd( p_playlist, [[item identifier] UTF8String] );
1981             }
1982         }
1983
1984                 [o_chosen_category_lbl setStringValue:[item title]];
1985         }
1986         else {
1987                 [o_chosen_category_lbl setStringValue:@"(none)"];
1988         }
1989 }
1990
1991 @end