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