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