]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: fixed progress bar position (refs #5742)
[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         if (OSX_LION)
415             [o_resize_view setImage: NULL];
416
417         if ([self styleMask] & NSResizableWindowMask)
418             [o_resize_view removeFromSuperviewWithoutNeedingDisplay];
419
420         [self display];
421     }
422     else
423         [o_video_view setFrame: [o_split_view frame]];
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             NSLog( @"showing pl" );
525         }
526         else
527         {
528             [o_video_view setHidden: YES];
529             [o_split_view setHidden: NO];
530             NSLog( @"show vout" );
531         }
532     }
533     else
534     {
535         [o_split_view setHidden: NO];
536         [o_playlist_table setHidden: NO];
537         [o_video_view setHidden: ![[VLCMain sharedInstance] activeVideoPlayback]];
538     }
539 }
540
541 - (void)setRepeatOne
542 {
543     [o_repeat_btn setImage: o_repeat_one_img];
544     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];   
545 }
546
547 - (void)setRepeatAll
548 {
549     [o_repeat_btn setImage: o_repeat_all_img];
550     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
551 }
552
553 - (void)setRepeatOff
554 {
555     [o_repeat_btn setImage: o_repeat_img];
556     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
557 }
558
559 - (IBAction)repeat:(id)sender
560 {
561     vlc_value_t looping,repeating;
562     intf_thread_t * p_intf = VLCIntf;
563     playlist_t * p_playlist = pl_Get( p_intf );
564
565     var_Get( p_playlist, "repeat", &repeating );
566     var_Get( p_playlist, "loop", &looping );
567
568     if( !repeating.b_bool && !looping.b_bool )
569     {
570         /* was: no repeating at all, switching to Repeat One */
571         [[VLCCoreInteraction sharedInstance] repeatOne];
572         [self setRepeatOne];
573     }
574     else if( repeating.b_bool && !looping.b_bool )
575     {
576         /* was: Repeat One, switching to Repeat All */
577         [[VLCCoreInteraction sharedInstance] repeatAll];
578         [self setRepeatAll];
579     }
580     else
581     {
582         /* was: Repeat All or bug in VLC, switching to Repeat Off */
583         [[VLCCoreInteraction sharedInstance] repeatOff];
584         [self setRepeatOff];
585     }
586 }
587
588 - (void)setShuffle
589 {
590     bool b_value;
591     playlist_t *p_playlist = pl_Get( VLCIntf );
592     b_value = var_GetBool( p_playlist, "random" );
593         if(b_value) {
594         [o_shuffle_btn setImage: o_shuffle_on_img];
595         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
596     }
597     else
598     {
599         [o_shuffle_btn setImage: o_shuffle_img];
600         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
601     }
602 }
603
604 - (IBAction)shuffle:(id)sender
605 {
606     [[VLCCoreInteraction sharedInstance] shuffle];
607     [self setShuffle];
608 }
609
610 - (IBAction)timeSliderAction:(id)sender
611 {
612     float f_updated;
613     input_thread_t * p_input;
614
615     switch( [[NSApp currentEvent] type] )
616     {
617         case NSLeftMouseUp:
618         case NSLeftMouseDown:
619         case NSLeftMouseDragged:
620             f_updated = [sender floatValue];
621             break;
622
623         default:
624             return;
625     }
626     p_input = pl_CurrentInput( VLCIntf );
627     if( p_input != NULL )
628     {
629         vlc_value_t time;
630         vlc_value_t pos;
631         NSString * o_time;
632         char psz_time[MSTRTIME_MAX_SIZE];
633
634         pos.f_float = f_updated / 10000.;
635         var_Set( p_input, "position", pos );
636         [o_time_sld setFloatValue: f_updated];
637
638         var_Get( p_input, "time", &time );
639
640         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
641         if( [o_time_fld timeRemaining] && dur != -1 )
642         {
643             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
644         }
645         else
646             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
647
648         [o_time_fld setStringValue: o_time];
649         [o_fspanel setStreamPos: f_updated andTime: o_time];
650         vlc_object_release( p_input );
651     }
652     [self drawFancyGradientEffectForTimeSlider];
653 }
654
655 - (IBAction)volumeAction:(id)sender
656 {
657     if (sender == o_volume_sld)
658         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
659     else if (sender == o_volume_down_btn)
660         [[VLCCoreInteraction sharedInstance] mute];
661     else
662         [[VLCCoreInteraction sharedInstance] setVolume: 400];
663 }
664
665 - (IBAction)effects:(id)sender
666 {
667     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
668 }
669
670 - (IBAction)fullscreen:(id)sender
671 {
672     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
673 }
674
675 - (IBAction)dropzoneButtonAction:(id)sender
676 {
677     [[[VLCMain sharedInstance] open] openFileGeneric];
678 }
679
680 #pragma mark -
681 #pragma mark overwritten default functionality
682 - (BOOL)canBecomeKeyWindow
683 {
684     return YES;
685 }
686
687 - (void)setTitle:(NSString *)title
688 {
689     if (b_dark_interface)
690         [o_titlebar_view setWindowTitle: title];
691     [super setTitle: title];
692 }
693
694 - (void)performZoom:(id)sender
695 {
696     if (b_dark_interface)
697         [self customZoom: sender];
698     else
699         [super performZoom: sender];
700 }
701
702 - (void)zoom:(id)sender
703 {
704     if (b_dark_interface)
705         [self customZoom: sender];
706     else
707         [super zoom: sender];
708 }
709
710 /**
711  * Given a proposed frame rectangle, return a modified version
712  * which will fit inside the screen.
713  *
714  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
715  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
716  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
717  *    Copyright (C) 1996 Free Software Foundation, Inc.
718  */
719 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
720 {
721     NSRect screenRect = [screen visibleFrame];
722     float difference;
723
724     /* Move top edge of the window inside the screen */
725     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
726     if (difference > 0)
727     {
728         frameRect.origin.y -= difference;
729     }
730
731     /* If the window is resizable, resize it (if needed) so that the
732      bottom edge is on the screen or can be on the screen when the user moves
733      the window */
734     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
735     if (_styleMask & NSResizableWindowMask)
736     {
737         float difference2;
738
739         difference2 = screenRect.origin.y - frameRect.origin.y;
740         difference2 -= difference;
741         // Take in account the space between the top of window and the top of the 
742         // screen which can be used to move the bottom of the window on the screen
743         if (difference2 > 0)
744         {
745             frameRect.size.height -= difference2;
746             frameRect.origin.y += difference2;
747         }
748
749         /* Ensure that resizing doesn't makewindow smaller than minimum */
750         difference2 = [self minSize].height - frameRect.size.height;
751         if (difference2 > 0)
752         {
753             frameRect.size.height += difference2;
754             frameRect.origin.y -= difference2;
755         }
756     }
757
758     return frameRect;
759 }
760
761 #define DIST 3
762
763 /**
764  Zooms the receiver.   This method calls the delegate method
765  windowShouldZoom:toFrame: to determine if the window should
766  be allowed to zoom to full screen.
767  *
768  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
769  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,   
770  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
771  *    Copyright (C) 1996 Free Software Foundation, Inc.
772  */
773 - (void) customZoom: (id)sender
774 {
775     NSRect maxRect = [[self screen] visibleFrame];
776     NSRect currentFrame = [self frame];
777
778     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)])
779     {
780         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
781     }
782
783     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
784
785     // Compare the new frame with the current one
786     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
787         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
788         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
789         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST))
790     {
791         // Already in zoomed mode, reset user frame, if stored
792         if ([self frameAutosaveName] != nil)
793         {
794             [self setFrame: previousSavedFrame display: YES animate: YES];
795             [self saveFrameUsingName: [self frameAutosaveName]];
796         }
797         return;
798     }
799
800     if ([self frameAutosaveName] != nil)
801     {
802         [self saveFrameUsingName: [self frameAutosaveName]];
803         previousSavedFrame = [self frame];
804     }
805
806     [self setFrame: maxRect display: YES animate: YES];
807 }
808
809 - (void)windowResizedOrMoved:(NSNotification *)notification
810 {
811     [self saveFrameUsingName: [self frameAutosaveName]];
812 }
813
814 #pragma mark -
815 #pragma mark Update interface and respond to foreign events
816 - (void)showDropZone
817 {
818     [o_right_split_view addSubview: o_dropzone_view];
819     [o_dropzone_view setFrame: [o_playlist_table frame]];
820     [[o_playlist_table animator] setHidden:YES];
821 }
822
823 - (void)hideDropZone
824 {
825     [o_dropzone_view removeFromSuperview];
826     [[o_playlist_table animator] setHidden: NO];
827 }
828
829 - (void)updateTimeSlider
830 {
831     input_thread_t * p_input;
832     p_input = pl_CurrentInput( VLCIntf );
833     if( p_input )
834     {
835         vlc_value_t time;
836         NSString * o_time;
837         vlc_value_t pos;
838         char psz_time[MSTRTIME_MAX_SIZE];
839         float f_updated;
840
841         var_Get( p_input, "position", &pos );
842         f_updated = 10000. * pos.f_float;
843         [o_time_sld setFloatValue: f_updated];
844
845         var_Get( p_input, "time", &time );
846
847         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
848         if( [o_time_fld timeRemaining] && dur != -1 )
849         {
850             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
851         }
852         else
853             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
854
855         if (dur == -1) {
856             [o_time_sld setEnabled: NO];
857             [o_time_sld setHidden: YES];
858         } else {
859             [o_time_sld setEnabled: YES];
860             [o_time_sld setHidden: NO];
861         }
862
863         [o_time_fld setStringValue: o_time];
864         [o_time_fld setNeedsDisplay:YES];
865         [o_fspanel setStreamPos: f_updated andTime: o_time];
866         vlc_object_release( p_input );
867     }
868     else
869     {
870         [o_time_sld setFloatValue: 0.0];
871         [o_time_fld setStringValue: @"00:00"];
872         [o_time_sld setEnabled: NO];
873         [o_time_sld setHidden: YES];
874     }
875         
876     [self performSelectorOnMainThread:@selector(drawFancyGradientEffectForTimeSlider) withObject:nil waitUntilDone:NO];
877 }
878
879 - (void)updateVolumeSlider
880 {
881     audio_volume_t i_volume;
882     playlist_t * p_playlist = pl_Get( VLCIntf );
883
884     i_volume = aout_VolumeGet( p_playlist );
885
886     if( i_volume != i_lastShownVolume )
887     {
888         i_lastShownVolume = i_volume;
889         int i_volume_step = 0;
890         i_volume_step = config_GetInt( VLCIntf->p_libvlc, "volume-step" );
891         [o_volume_sld setFloatValue: (float)i_lastShownVolume / i_volume_step];
892         [o_fspanel setVolumeLevel: (float)i_lastShownVolume / i_volume_step];
893     }
894 }
895
896 - (void)updateName
897 {
898     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
899     input_thread_t * p_input;
900     p_input = pl_CurrentInput( VLCIntf );
901     if( p_input )
902     {
903         NSString *aString;
904         char *format = var_InheritString( VLCIntf, "input-title-format" );
905         char *formated = str_format_meta( p_input, format );
906         free( format );
907         aString = [NSString stringWithUTF8String:formated];
908         free( formated );
909
910         char *uri = input_item_GetURI( input_GetItem( p_input ) );
911
912         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
913         if ([o_url isFileURL])
914             [self setRepresentedURL: o_url];
915         else
916             [self setRepresentedURL: nil];
917         free( uri );
918
919         if ([aString isEqualToString:@""])
920         {
921             if ([o_url isFileURL])
922                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
923             else
924                 aString = [o_url absoluteString];
925         }
926
927         [self setTitle: aString];
928         [o_fspanel setStreamTitle: aString];
929     }
930     else
931     {
932         [self setTitle: _NS("VLC media player")];
933         [self setRepresentedURL: nil];
934     }
935
936     [o_pool release];
937 }
938
939 - (void)updateWindow
940 {
941     bool b_input = false;
942     bool b_plmul = false;
943     bool b_control = false;
944     bool b_seekable = false;
945     bool b_chapters = false;
946
947     playlist_t * p_playlist = pl_Get( VLCIntf );
948
949     PL_LOCK;
950     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
951     PL_UNLOCK;
952
953     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
954
955     bool b_buffering = NO;
956
957     if( ( b_input = ( p_input != NULL ) ) )
958     {
959         /* seekable streams */
960         cachedInputState = input_GetState( p_input );
961         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
962             b_buffering = YES;
963
964         /* seekable streams */
965         b_seekable = var_GetBool( p_input, "can-seek" );
966
967         /* check whether slow/fast motion is possible */
968         b_control = var_GetBool( p_input, "can-rate" );
969
970         /* chapters & titles */
971         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
972
973         if (cachedInputState == PLAYING_S || b_buffering == YES)
974             [self makeKeyAndOrderFront: nil];
975         vlc_object_release( p_input );
976     }
977
978     if( b_buffering )
979     {
980         [o_progress_bar startAnimation:self];
981         [o_progress_bar setIndeterminate:YES];
982         [o_progress_bar setHidden:NO];
983     } else {
984         [o_progress_bar stopAnimation:self];
985         [o_progress_bar setHidden:YES];
986     }
987
988     [o_stop_btn setEnabled: b_input];
989     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
990     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
991     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
992
993     [o_time_sld setEnabled: b_seekable];
994     [self updateTimeSlider];
995     [o_fspanel setSeekable: b_seekable];
996
997     PL_LOCK;
998     if (playlist_CurrentSize( p_playlist ) >= 1)
999         [self hideDropZone];
1000     else
1001         [self showDropZone];
1002     PL_UNLOCK;
1003 }
1004
1005 - (void)setPause
1006 {
1007     [o_play_btn setImage: o_pause_img];
1008     [o_play_btn setAlternateImage: o_pause_pressed_img];
1009     [o_play_btn setToolTip: _NS("Pause")];
1010     [o_fspanel setPause];
1011 }
1012
1013 - (void)setPlay
1014 {
1015     [o_play_btn setImage: o_play_img];
1016     [o_play_btn setAlternateImage: o_play_pressed_img];
1017     [o_play_btn setToolTip: _NS("Play")];
1018     [o_fspanel setPlay];
1019 }
1020
1021 - (void)drawFancyGradientEffectForTimeSlider
1022 {
1023     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1024     float f_value = ([o_time_sld frame].size.width - [o_time_sld frame].origin.x - [o_time_sld knobPosition]) * -1.0;
1025     if (f_value > 7.5)
1026     {
1027         NSRect oldFrame = [o_time_sld_fancygradient_view frame];
1028         if (f_value != oldFrame.size.width)
1029         {
1030             [o_time_sld_fancygradient_view setHidden: NO];
1031             [o_time_sld_fancygradient_view setFrame: NSMakeRect( oldFrame.origin.x, oldFrame.origin.y, f_value, oldFrame.size.height )];
1032             [o_time_sld_fancygradient_view setNeedsDisplay:YES];
1033         }
1034     }
1035     else
1036     {
1037         [o_time_sld_fancygradient_view setHidden: YES];
1038     }
1039     [o_pool release];
1040 }
1041
1042 #pragma mark -
1043 #pragma mark Video Output handling
1044
1045 - (id)videoView
1046 {
1047     vout_thread_t *p_vout = getVout();
1048     if (config_GetInt( VLCIntf, "embedded-video" ))
1049     {
1050         if ([o_video_view window] != self)
1051         {
1052             [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1053             [o_video_view setFrame: [o_split_view frame]];
1054             [[self contentView] addSubview:o_video_view positioned:NSWindowAbove relativeTo:nil];
1055         }
1056         b_nonembedded = NO;
1057     }
1058     else
1059     {
1060         [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1061         if (o_nonembedded_window)
1062             [o_nonembedded_window release];
1063
1064         o_nonembedded_window = [[VLCWindow alloc] initWithContentRect:[o_video_view frame] styleMask: NSBorderlessWindowMask|NSResizableWindowMask backing:NSBackingStoreBuffered defer:YES];
1065         [o_nonembedded_window setFrame:[o_video_view frame] display:NO];
1066         [o_nonembedded_window setBackgroundColor: [NSColor blackColor]];
1067         [o_nonembedded_window setMovableByWindowBackground: YES];
1068         [o_nonembedded_window setCanBecomeKeyWindow: YES];
1069         [o_nonembedded_window setHasShadow:YES];
1070         [o_nonembedded_window setContentView: o_video_view];
1071         [o_nonembedded_window setLevel:NSNormalWindowLevel];
1072         [o_nonembedded_window useOptimizedDrawing: YES];
1073         [o_nonembedded_window center];
1074         [o_nonembedded_window makeKeyAndOrderFront:self];
1075         [o_nonembedded_window orderFront:self animate:YES];
1076         [o_nonembedded_window setReleasedWhenClosed:NO];
1077         b_nonembedded = YES;
1078     }
1079
1080     if (p_vout)
1081     {
1082         if( var_GetBool( p_vout, "video-on-top" ) )
1083             [[o_video_view window] setLevel: NSStatusWindowLevel];
1084         else
1085             [[o_video_view window] setLevel: NSNormalWindowLevel];
1086         vlc_object_release( p_vout );
1087     }
1088     return o_video_view;
1089 }
1090
1091 - (void)setVideoplayEnabled
1092 {
1093     BOOL b_videoPlayback = [[VLCMain sharedInstance] activeVideoPlayback];
1094
1095     if (!b_nonembedded)
1096         [o_playlist_btn setEnabled: b_videoPlayback];
1097     else
1098     {
1099         [o_playlist_btn setEnabled: NO];
1100         if (!b_videoPlayback)
1101             [o_nonembedded_window orderOut: nil];
1102     }
1103     if( OSX_LION && b_nativeFullscreenMode )
1104     {
1105         if( [NSApp presentationOptions] == NSApplicationPresentationFullScreen )
1106             [o_bottombar_view setHidden: b_videoPlayback];
1107         else
1108             [o_bottombar_view setHidden: NO];
1109         if (!b_videoPlayback)
1110             [o_fspanel setNonActive: nil];
1111     }
1112 }
1113
1114 - (void)resizeWindow
1115 {
1116     if ( !b_fullscreen && !(OSX_LION && [NSApp presentationOptions] == NSApplicationPresentationFullScreen && b_nativeFullscreenMode) )
1117     {
1118         NSPoint topleftbase;
1119         NSPoint topleftscreen;
1120         NSRect new_frame;
1121         topleftbase.x = 0;
1122         topleftbase.y = [self frame].size.height;
1123         topleftscreen = [self convertBaseToScreen: topleftbase];
1124
1125         /* Calculate the window's new size */
1126         new_frame.size.width = [self frame].size.width - [o_video_view frame].size.width + nativeVideoSize.width;
1127         if (b_dark_interface)
1128             new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + nativeVideoSize.height + [o_titlebar_view frame].size.height;
1129         else
1130             new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + nativeVideoSize.height;
1131
1132         new_frame.origin.x = topleftscreen.x;
1133         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
1134
1135         [[self animator] setFrame:new_frame display:YES];
1136     }
1137 }
1138
1139 - (void)setNativeVideoSize:(NSSize)size
1140 {
1141     if (size.width != nativeVideoSize.width || size.height != nativeVideoSize.height )
1142     {
1143         nativeVideoSize = size;
1144         [self resizeWindow];
1145     }
1146 }
1147
1148 #pragma mark -
1149 #pragma mark Fullscreen support
1150 - (void)showFullscreenController
1151 {
1152      if (b_fullscreen && [[VLCMain sharedInstance] activeVideoPlayback] )
1153         [o_fspanel fadeIn];
1154 }
1155
1156 - (BOOL)isFullscreen
1157 {
1158     return b_fullscreen;
1159 }
1160
1161 - (void)lockFullscreenAnimation
1162 {
1163     [o_animation_lock lock];
1164 }
1165
1166 - (void)unlockFullscreenAnimation
1167 {
1168     [o_animation_lock unlock];
1169 }
1170
1171 - (void)enterFullscreen
1172 {
1173     NSMutableDictionary *dict1, *dict2;
1174     NSScreen *screen;
1175     NSRect screen_rect;
1176     NSRect rect;
1177     vout_thread_t *p_vout = getVout();
1178     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1179
1180     if( p_vout )
1181         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_GetInteger( p_vout, "video-device" )];
1182
1183     [self lockFullscreenAnimation];
1184
1185     if (!screen)
1186     {
1187         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
1188         screen = [self screen];
1189     }
1190     if (!screen)
1191     {
1192         msg_Dbg( VLCIntf, "Using deepest screen" );
1193         screen = [NSScreen deepestScreen];
1194     }
1195
1196     if( p_vout )
1197         vlc_object_release( p_vout );
1198
1199     screen_rect = [screen frame];
1200
1201     [o_fullscreen_btn setState: YES];
1202
1203     [NSCursor setHiddenUntilMouseMoves: YES];
1204
1205     if( blackout_other_displays )
1206         [screen blackoutOtherScreens];
1207
1208     /* Make sure we don't see the window flashes in float-on-top mode */
1209     i_originalLevel = [self level];
1210     [self setLevel:NSNormalWindowLevel];
1211
1212     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
1213     if (!o_fullscreen_window)
1214     {
1215         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
1216
1217         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
1218         rect.origin.x += [self frame].origin.x;
1219         rect.origin.y += [self frame].origin.y;
1220         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
1221         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
1222         [o_fullscreen_window setCanBecomeKeyWindow: YES];
1223
1224         if (![self isVisible] || [self alphaValue] == 0.0)
1225         {
1226             /* We don't animate if we are not visible, instead we
1227              * simply fade the display */
1228             CGDisplayFadeReservationToken token;
1229
1230             if( blackout_other_displays )
1231             {
1232                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1233                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1234             }
1235
1236             if ([screen isMainScreen])
1237             {
1238                 if (OSX_LEOPARD)
1239                     SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1240                 else
1241                     [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1242             }
1243
1244             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1245             [o_temp_view setFrame:[o_video_view frame]];
1246             [o_fullscreen_window setContentView:o_video_view];
1247
1248             [o_fullscreen_window makeKeyAndOrderFront:self];
1249             [o_fullscreen_window orderFront:self animate:YES];
1250
1251             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
1252             [o_fullscreen_window setLevel:NSNormalWindowLevel];
1253
1254             if( blackout_other_displays )
1255             {
1256                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1257                 CGReleaseDisplayFadeReservation( token );
1258             }
1259
1260             /* Will release the lock */
1261             [self hasBecomeFullscreen];
1262
1263             return;
1264         }
1265
1266         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1267         NSDisableScreenUpdates();
1268         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
1269         [o_temp_view setFrame:[o_video_view frame]];
1270         [o_fullscreen_window setContentView:o_video_view];
1271         [o_fullscreen_window makeKeyAndOrderFront:self];
1272         NSEnableScreenUpdates();
1273     }
1274
1275     /* We are in fullscreen (and no animation is running) */
1276     if (b_fullscreen)
1277     {
1278         /* Make sure we are hidden */
1279         [super orderOut: self];
1280         [self unlockFullscreenAnimation];
1281         return;
1282     }
1283
1284     if (o_fullscreen_anim1)
1285     {
1286         [o_fullscreen_anim1 stopAnimation];
1287         [o_fullscreen_anim1 release];
1288     }
1289     if (o_fullscreen_anim2)
1290     {
1291         [o_fullscreen_anim2 stopAnimation];
1292         [o_fullscreen_anim2 release];
1293     }
1294
1295     if ([screen isMainScreen])
1296     {
1297         if (OSX_LEOPARD)
1298             SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1299         else
1300             [NSApp setPresentationOptions:(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1301     }
1302
1303     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
1304     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
1305
1306     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
1307     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
1308
1309     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1310     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1311     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
1312
1313     /* Strategy with NSAnimation allocation:
1314      - Keep at most 2 animation at a time
1315      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
1316      */
1317     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1318     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1319
1320     [dict1 release];
1321     [dict2 release];
1322
1323     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1324     [o_fullscreen_anim1 setDuration: 0.3];
1325     [o_fullscreen_anim1 setFrameRate: 30];
1326     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1327     [o_fullscreen_anim2 setDuration: 0.2];
1328     [o_fullscreen_anim2 setFrameRate: 30];
1329
1330     [o_fullscreen_anim2 setDelegate: self];
1331     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1332
1333     [o_fullscreen_anim1 startAnimation];
1334     /* fullscreenAnimation will be unlocked when animation ends */
1335 }
1336
1337 - (void)hasBecomeFullscreen
1338 {
1339     [o_fullscreen_window makeFirstResponder: o_video_view];
1340
1341     [o_fullscreen_window makeKeyWindow];
1342     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1343
1344     /* tell the fspanel to move itself to front next time it's triggered */
1345     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1346     [o_fspanel setActive: nil];
1347
1348     if([self isVisible])
1349         [super orderOut: self];
1350
1351     [o_fspanel setActive: nil];
1352
1353     b_fullscreen = YES;
1354     [self unlockFullscreenAnimation];
1355 }
1356
1357 - (void)leaveFullscreen
1358 {
1359     [self leaveFullscreenAndFadeOut: NO];
1360 }
1361
1362 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1363 {
1364     NSMutableDictionary *dict1, *dict2;
1365     NSRect frame;
1366     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1367
1368     [self lockFullscreenAnimation];
1369
1370     b_fullscreen = NO;
1371     [o_fullscreen_btn setState: NO];
1372
1373     /* We always try to do so */
1374     if (!(OSX_LION && b_nativeFullscreenMode))
1375         [NSScreen unblackoutScreens];
1376     vout_thread_t *p_vout = getVout();
1377     if (p_vout)
1378     {
1379         if( var_GetBool( p_vout, "video-on-top" ) )
1380             [[o_video_view window] setLevel: NSStatusWindowLevel];
1381         else
1382             [[o_video_view window] setLevel: NSNormalWindowLevel];
1383         vlc_object_release( p_vout );
1384     }
1385     [[o_video_view window] makeKeyAndOrderFront: nil];
1386
1387     /* Don't do anything if o_fullscreen_window is already closed */
1388     if (!o_fullscreen_window)
1389     {
1390         [self unlockFullscreenAnimation];
1391         return;
1392     }
1393
1394     if (fadeout)
1395     {
1396         /* We don't animate if we are not visible, instead we
1397          * simply fade the display */
1398         CGDisplayFadeReservationToken token;
1399
1400         if( blackout_other_displays )
1401         {
1402             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1403             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1404         }
1405
1406         [o_fspanel setNonActive: nil];
1407         if (OSX_LEOPARD)
1408             SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1409         else
1410             [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1411
1412         /* Will release the lock */
1413         [self hasEndedFullscreen];
1414
1415         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1416          * here */
1417         b_window_is_invisible = YES;
1418
1419         if( blackout_other_displays )
1420         {
1421             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1422             CGReleaseDisplayFadeReservation( token );
1423         }
1424
1425         return;
1426     }
1427
1428     [self setAlphaValue: 0.0];
1429     [self orderFront: self];
1430     [[o_video_view window] orderFront: self];
1431
1432     [o_fspanel setNonActive: nil];
1433     if (OSX_LEOPARD)
1434         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1435     else
1436         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1437
1438     if (o_fullscreen_anim1)
1439     {
1440         [o_fullscreen_anim1 stopAnimation];
1441         [o_fullscreen_anim1 release];
1442     }
1443     if (o_fullscreen_anim2)
1444     {
1445         [o_fullscreen_anim2 stopAnimation];
1446         [o_fullscreen_anim2 release];
1447     }
1448
1449     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1450     frame.origin.x += [self frame].origin.x;
1451     frame.origin.y += [self frame].origin.y;
1452
1453     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1454     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
1455     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1456
1457     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1458     [dict2 release];
1459
1460     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1461     [o_fullscreen_anim2 setDuration: 0.3];
1462     [o_fullscreen_anim2 setFrameRate: 30];
1463
1464     [o_fullscreen_anim2 setDelegate: self];
1465
1466     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1467
1468     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1469     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1470     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1471
1472     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1473     [dict1 release];
1474
1475     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1476     [o_fullscreen_anim1 setDuration: 0.2];
1477     [o_fullscreen_anim1 setFrameRate: 30];
1478     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1479
1480     /* Make sure o_fullscreen_window is the frontmost window */
1481     [o_fullscreen_window orderFront: self];
1482
1483     [o_fullscreen_anim1 startAnimation];
1484     /* fullscreenAnimation will be unlocked when animation ends */
1485 }
1486
1487 - (void)hasEndedFullscreen
1488 {
1489     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1490     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1491     NSDisableScreenUpdates();
1492     [o_video_view retain];
1493     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1494     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1495     [o_video_view release];
1496     [o_video_view setFrame:[o_temp_view frame]];
1497     [self makeFirstResponder: o_video_view];
1498     if ([self isVisible])
1499         [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1500     [o_fullscreen_window orderOut: self];
1501     NSEnableScreenUpdates();
1502
1503     [o_fullscreen_window release];
1504     o_fullscreen_window = nil;
1505     [self setLevel:i_originalLevel];
1506
1507     [self unlockFullscreenAnimation];
1508 }
1509
1510 - (void)animationDidEnd:(NSAnimation*)animation
1511 {
1512     NSArray *viewAnimations;
1513     if( o_makekey_anim == animation )
1514     {
1515         [o_makekey_anim release];
1516         return;
1517     }
1518     if ([animation currentValue] < 1.0)
1519         return;
1520
1521     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1522     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1523     if ([viewAnimations count] >=1 &&
1524         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
1525     {
1526         /* Fullscreen ended */
1527         [self hasEndedFullscreen];
1528     }
1529     else
1530     {
1531         /* Fullscreen started */
1532         [self hasBecomeFullscreen];
1533     }
1534 }
1535
1536 - (void)orderOut: (id)sender
1537 {
1538     /* Make sure we leave fullscreen */
1539     if (!(OSX_LION && b_nativeFullscreenMode))
1540         [self leaveFullscreenAndFadeOut: YES];
1541
1542     [super orderOut: sender];
1543 }
1544
1545 - (void)makeKeyAndOrderFront: (id)sender
1546 {
1547     /* Hack
1548      * when we exit fullscreen and fade out, we may endup in
1549      * having a window that is faded. We can't have it fade in unless we
1550      * animate again. */
1551
1552     if(!b_window_is_invisible)
1553     {
1554         /* Make sure we don't do it too much */
1555         [super makeKeyAndOrderFront: sender];
1556         return;
1557     }
1558
1559     [super setAlphaValue:0.0f];
1560     [super makeKeyAndOrderFront: sender];
1561
1562     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1563     [dict setObject:self forKey:NSViewAnimationTargetKey];
1564     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1565
1566     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1567     [dict release];
1568
1569     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1570     [o_makekey_anim setDuration: 0.1];
1571     [o_makekey_anim setFrameRate: 30];
1572     [o_makekey_anim setDelegate: self];
1573
1574     [o_makekey_anim startAnimation];
1575     b_window_is_invisible = NO;
1576
1577     /* fullscreenAnimation will be unlocked when animation ends */
1578 }
1579
1580 /* Make sure setFrame gets executed on main thread especially if we are animating.
1581  * (Thus we won't block the video output thread) */
1582 - (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate
1583 {
1584     struct { NSRect frame; BOOL display; BOOL animate;} args;
1585     NSData *packedargs;
1586
1587     args.frame = frame;
1588     args.display = display;
1589     args.animate = animate;
1590
1591     packedargs = [NSData dataWithBytes:&args length:sizeof(args)];
1592
1593     [self performSelectorOnMainThread:@selector(setFrameOnMainThread:)
1594                            withObject: packedargs waitUntilDone: YES];
1595 }
1596
1597 - (void)setFrameOnMainThread:(NSData*)packedargs
1598 {
1599     struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes];
1600
1601     if( args->animate )
1602     {
1603         /* Make sure we don't block too long and set up a non blocking animation */
1604         NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
1605                                self, NSViewAnimationTargetKey,
1606                                [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey,
1607                                [NSValue valueWithRect:args->frame], NSViewAnimationEndFrameKey, nil];
1608
1609         NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1610
1611         [anim setAnimationBlockingMode: NSAnimationNonblocking];
1612         [anim setDuration: 0.4];
1613         [anim setFrameRate: 30];
1614         [anim startAnimation];
1615
1616         [anim release];
1617     }
1618     else {
1619         [super setFrame:args->frame display:args->display animate:args->animate];
1620     }
1621 }
1622
1623 #pragma mark -
1624 #pragma mark Lion's native fullscreen handling
1625 - (void)windowWillEnterFullScreen:(NSNotification *)notification
1626 {
1627     [o_video_view setFrame: [[self contentView] frame]];
1628     [NSCursor setHiddenUntilMouseMoves: YES];
1629     b_fullscreen = YES;
1630     [o_fspanel setVoutWasUpdated: (int)[[self screen] displayID]];
1631
1632     if (b_dark_interface)
1633     {
1634         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
1635
1636         NSRect winrect;
1637         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1638         winrect = [self frame];
1639
1640         winrect.size.height = winrect.size.height - f_titleBarHeight;
1641         [self setFrame: winrect display:NO animate:NO];
1642         winrect = [o_split_view frame];
1643         winrect.size.height = winrect.size.height + f_titleBarHeight;
1644         [o_split_view setFrame: winrect];
1645     }
1646 }
1647
1648 - (void)windowWillExitFullScreen:(NSNotification *)notification
1649 {
1650     [o_video_view setFrame: [o_split_view frame]];
1651     [NSCursor setHiddenUntilMouseMoves: NO];
1652     [o_fspanel setNonActive: nil];
1653     b_fullscreen = NO;
1654
1655     if (b_dark_interface)
1656     {
1657         NSRect winrect;
1658         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
1659         winrect = [self frame];
1660         
1661         [o_titlebar_view setFrame: NSMakeRect( 0, winrect.size.height - f_titleBarHeight,
1662                                               winrect.size.width, f_titleBarHeight )];
1663         [[self contentView] addSubview: o_titlebar_view];
1664         
1665         winrect.size.height = winrect.size.height + f_titleBarHeight;
1666         [self setFrame: winrect display:NO animate:NO];
1667         winrect = [o_split_view frame];
1668         winrect.size.height = winrect.size.height - f_titleBarHeight;
1669         [o_split_view setFrame: winrect];
1670         [o_video_view setFrame: winrect];
1671     }
1672 }
1673
1674 #pragma mark -
1675 #pragma mark split view delegate
1676 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex
1677 {
1678     if (dividerIndex == 0)
1679         return 200.0;
1680     else
1681         return proposedMin;
1682 }
1683
1684 - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)dividerIndex
1685 {
1686     if (dividerIndex == 0)
1687         return ([self frame].size.width - 300.0);
1688     else
1689         return proposedMax;
1690 }
1691
1692 #pragma mark -
1693 #pragma mark Side Bar Data handling
1694 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1695 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
1696 {
1697         //Works the same way as the NSOutlineView data source: `nil` means a parent item
1698         if(item==nil) {
1699                 return [o_sidebaritems count];
1700         }
1701         else {
1702                 return [[item children] count];
1703         }
1704 }
1705
1706
1707 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1708 {
1709     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1710         if(item==nil) {
1711                 return [o_sidebaritems objectAtIndex:index];
1712         }
1713         else {
1714                 return [[item children] objectAtIndex:index];
1715         }
1716 }
1717
1718
1719 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1720 {
1721         return [item title];
1722 }
1723
1724 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1725 {
1726         [item setTitle:object];
1727 }
1728
1729 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1730 {
1731         return [item hasChildren];
1732 }
1733
1734
1735 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1736 {
1737     if ([[item identifier] isEqualToString: @"playlist"])
1738         return YES;
1739
1740         return [item hasBadge];
1741 }
1742
1743
1744 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1745 {
1746     if ([[item identifier] isEqualToString: @"playlist"]) {
1747         playlist_t * p_playlist = pl_Get( VLCIntf );
1748         NSInteger i_playlist_size;
1749
1750         PL_LOCK;
1751         i_playlist_size = playlist_CurrentSize( p_playlist );
1752         PL_UNLOCK;
1753
1754         return i_playlist_size;
1755     }
1756         return [item badgeValue];
1757 }
1758
1759
1760 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1761 {
1762         return [item hasIcon];
1763 }
1764
1765
1766 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1767 {
1768         return [item icon];
1769 }
1770
1771 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1772 {
1773         if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1774                 NSMenu * m = [[NSMenu alloc] init];
1775                 if (item != nil)
1776                         [m addItemWithTitle:[item title] action:nil keyEquivalent:@""];
1777                 return [m autorelease];
1778         }
1779         return nil;
1780 }
1781
1782 #pragma mark -
1783 #pragma mark Side Bar Delegate Methods
1784 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1785 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1786 {
1787         if([[group identifier] isEqualToString:@"library"])
1788                 return YES;
1789
1790         return NO;
1791 }
1792
1793 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1794 {
1795         NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1796
1797         //Set the label text to represent the new selection
1798     if([selectedIndexes count]==1) {
1799                 NSString *title = [[o_sidebar_view itemAtRow:[selectedIndexes firstIndex]] title];
1800
1801                 [o_chosen_category_lbl setStringValue:title];
1802         }
1803         else {
1804                 [o_chosen_category_lbl setStringValue:@"(none)"];
1805         }
1806 }
1807
1808 @end