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