]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindow.m
macosx: simplified the way we set the window's title
[vlc] / modules / gui / macosx / MainWindow.m
1 /*****************************************************************************
2  * MainWindow.h: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2011 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 "MainWindow.h"
28 #import "intf.h"
29 #import "CoreInteraction.h"
30 #import "AudioEffects.h"
31 #import "MainMenu.h"
32 #import "open.h"
33 #import "controls.h" // TODO: remove me
34 #import "SideBarItem.h"
35 #import <vlc_playlist.h>
36 #import <vlc_aout_intf.h>
37 #import <vlc_url.h>
38 #import <vlc_strings.h>
39 #import <vlc_services_discovery.h>
40
41 @implementation VLCMainWindow
42 static VLCMainWindow *_o_sharedInstance = nil;
43
44 + (VLCMainWindow *)sharedInstance
45 {
46     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
47 }
48
49 #pragma mark -
50 #pragma mark Initialization
51
52 - (id)init
53 {
54     if( _o_sharedInstance)
55     {
56         [self dealloc];
57         return _o_sharedInstance;
58     }
59     else
60     {
61         o_fspanel = [[VLCFSPanel alloc] init];
62         _o_sharedInstance = [super init];
63     }
64
65     return _o_sharedInstance;
66 }
67
68 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
69                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
70 {
71 //     styleMask ^= NSTexturedBackgroundWindowMask;
72
73     self = [super initWithContentRect:contentRect styleMask:styleMask //& ~NSTitledWindowMask
74                               backing:backingType defer:flag];
75
76     [[VLCMain sharedInstance] updateTogglePlaylistState];
77
78     /* we want to be moveable regardless of our style */
79     [self setMovableByWindowBackground: YES];
80
81     /* we don't want this window to be restored on relaunch */
82     if ([self respondsToSelector:@selector(setRestorable:)])
83         [self setRestorable:NO];
84
85     return self;
86 }
87
88 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
89 {
90     /* We indeed want to prioritize Cocoa key equivalent against libvlc,
91      so we perform the menu equivalent now. */
92     if([[NSApp mainMenu] performKeyEquivalent:o_event])
93         return TRUE;
94
95     return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event] || [(VLCControls *)[[VLCMain sharedInstance] controls] keyEvent:o_event];
96 }
97
98 - (void)dealloc
99 {
100     config_PutInt( VLCIntf->p_libvlc, "volume", i_lastShownVolume );
101     [o_sidebaritems release];
102     [super dealloc];
103 }
104
105 - (void)awakeFromNib
106 {
107     /* setup the styled interface */
108     b_dark_interface = config_GetInt( VLCIntf, "macosx-interfacestyle" );
109     i_lastShownVolume = -1;
110
111     [o_play_btn setToolTip: _NS("Play/Pause")];
112     [o_bwd_btn setToolTip: _NS("Backward")];
113     [o_fwd_btn setToolTip: _NS("Forward")];
114     [o_stop_btn setToolTip: _NS("Stop")];
115     [o_playlist_btn setToolTip: _NS("Show/Hide Playlist")];
116     [o_repeat_btn setToolTip: _NS("Repeat")];
117     [o_shuffle_btn setToolTip: _NS("Shuffle")];
118     [o_effects_btn setToolTip: _NS("Effects")];
119     [o_fullscreen_btn setToolTip: _NS("Toggle Fullscreen mode")];
120     [[o_search_fld cell] setPlaceholderString: _NS("Search")];
121     [o_volume_sld setToolTip: _NS("Volume")];
122     [o_volume_down_btn setToolTip: _NS("Mute")];
123     [o_volume_up_btn setToolTip: _NS("Full Volume")];
124     [o_time_sld setToolTip: _NS("Position")];
125     [o_dropzone_btn setTitle: _NS("Open media...")];
126     [o_dropzone_lbl setStringValue: _NS("Drop media here")];
127
128     if (!b_dark_interface) {
129         [o_bottombar_view setImage: [NSImage imageNamed:@"bottom-background"]];
130         [o_bwd_btn setImage: [NSImage imageNamed:@"back"]];
131         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed"]];
132         o_play_img = [[NSImage imageNamed:@"play"] retain];
133         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed"] retain];
134         o_pause_img = [[NSImage imageNamed:@"pause"] retain];
135         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed"] retain];
136         [o_fwd_btn setImage: [NSImage imageNamed:@"forward"]];
137         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed"]];
138         [o_stop_btn setImage: [NSImage imageNamed:@"stop"]];
139         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed"]];
140         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist"]];
141         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed"]];
142         o_repeat_img = [[NSImage imageNamed:@"repeat"] retain];
143         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed"] retain];
144         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all"] retain];
145         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-pressed"] retain];
146         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one"] retain];
147         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-pressed"] retain];
148         o_shuffle_img = [[NSImage imageNamed:@"shuffle"] retain];
149         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed"] retain];
150         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue"] retain];
151         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed"] retain];
152         [o_time_sld_left_view setImage: [NSImage imageNamed:@"progression-track-wrapper-left"]];
153         [o_time_sld_middle_view setImage: [NSImage imageNamed:@"progression-track-wrapper-middle"]];
154         [o_time_sld_right_view setImage: [NSImage imageNamed:@"progression-track-wrapper-right"]];
155         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low"]];
156         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track"]];
157         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high"]];
158         [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons"]];
159         [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed"]];
160         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons"]];
161         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed"]];
162         [o_time_sld_fancygradient_view loadImagesInDarkStyle:NO];
163     }
164     else
165     {
166         /* TODO: we also need to change the window style here... */
167         [o_bottombar_view setImage: [NSImage imageNamed:@"bottom-background_dark"]];
168         [o_bwd_btn setImage: [NSImage imageNamed:@"back_dark"]];
169         [o_bwd_btn setAlternateImage: [NSImage imageNamed:@"back-pressed_dark"]];
170         o_play_img = [[NSImage imageNamed:@"play_dark"] retain];
171         o_play_pressed_img = [[NSImage imageNamed:@"play-pressed_dark"] retain];
172         o_pause_img = [[NSImage imageNamed:@"pause_dark"] retain];
173         o_pause_pressed_img = [[NSImage imageNamed:@"pause-pressed_dark"] retain];
174         [o_fwd_btn setImage: [NSImage imageNamed:@"forward_dark"]];
175         [o_fwd_btn setAlternateImage: [NSImage imageNamed:@"forward-pressed_dark"]];
176         [o_stop_btn setImage: [NSImage imageNamed:@"stop_dark"]];
177         [o_stop_btn setAlternateImage: [NSImage imageNamed:@"stop-pressed_dark"]];
178         [o_playlist_btn setImage: [NSImage imageNamed:@"playlist_dark"]];
179         [o_playlist_btn setAlternateImage: [NSImage imageNamed:@"playlist-pressed_dark"]];
180         o_repeat_img = [[NSImage imageNamed:@"repeat_dark"] retain];
181         o_repeat_pressed_img = [[NSImage imageNamed:@"repeat-pressed_dark"] retain];
182         o_repeat_all_img  = [[NSImage imageNamed:@"repeat-all-blue_dark"] retain];
183         o_repeat_all_pressed_img = [[NSImage imageNamed:@"repeat-all-blue-pressed_dark"] retain];
184         o_repeat_one_img = [[NSImage imageNamed:@"repeat-one-blue_dark"] retain];
185         o_repeat_one_pressed_img = [[NSImage imageNamed:@"repeat-one-blue-pressed_dark"] retain];
186         o_shuffle_img = [[NSImage imageNamed:@"shuffle_dark"] retain];
187         o_shuffle_pressed_img = [[NSImage imageNamed:@"shuffle-pressed_dark"] retain];
188         o_shuffle_on_img = [[NSImage imageNamed:@"shuffle-blue_dark"] retain];
189         o_shuffle_on_pressed_img = [[NSImage imageNamed:@"shuffle-blue-pressed_dark"] retain];
190         [o_time_fld setTextColor: [NSColor colorWithCalibratedRed:229.0 green:229.0 blue:229.0 alpha:100.0]];
191         [o_time_sld_left_view setImage: [NSImage imageNamed:@"progression-track-wrapper-left_dark"]];
192         [o_time_sld_middle_view setImage: [NSImage imageNamed:@"progression-track-wrapper-middle_dark"]];
193         [o_time_sld_right_view setImage: [NSImage imageNamed:@"progression-track-wrapper-right_dark"]];
194         [o_volume_down_btn setImage: [NSImage imageNamed:@"volume-low_dark"]];
195         [o_volume_track_view setImage: [NSImage imageNamed:@"volume-slider-track_dark"]];
196         [o_volume_up_btn setImage: [NSImage imageNamed:@"volume-high_dark"]];
197         [o_effects_btn setImage: [NSImage imageNamed:@"effects-double-buttons_dark"]];
198         [o_effects_btn setAlternateImage: [NSImage imageNamed:@"effects-double-buttons-pressed_dark"]];
199         [o_fullscreen_btn setImage: [NSImage imageNamed:@"fullscreen-double-buttons_dark"]];
200         [o_fullscreen_btn setAlternateImage: [NSImage imageNamed:@"fullscreen-double-buttons-pressed_dark"]];
201         [o_time_sld_fancygradient_view loadImagesInDarkStyle:YES];
202     }
203     [o_repeat_btn setImage: o_repeat_img];
204     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
205     [o_shuffle_btn setImage: o_shuffle_img];
206     [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
207     [o_play_btn setImage: o_play_img];
208     [o_play_btn setAlternateImage: o_play_pressed_img];
209
210     /* interface builder action */
211     [self setDelegate: self];
212     [self setExcludedFromWindowsMenu: YES];
213     // Set that here as IB seems to be buggy
214     [self setContentMinSize:NSMakeSize(500., 288.)];
215     [self setTitle: _NS("VLC media player")];
216     [o_playlist_btn setEnabled:NO];
217     [o_video_view setFrame: [o_split_view frame]];
218     o_temp_view = [[NSView alloc] init];
219     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
220     [o_dropzone_view setFrame: [o_playlist_table frame]];
221
222     /* create the sidebar */
223     o_sidebaritems = [[NSMutableArray alloc] init];
224     SideBarItem *libraryItem = [SideBarItem itemWithTitle:_NS("LIBRARY") identifier:@"library"];
225     SideBarItem *playlistItem = [SideBarItem itemWithTitle:_NS("Playlist") identifier:@"playlist"];
226     [playlistItem setIcon: [NSImage imageNamed:@"document-music-playlist"]];
227     SideBarItem *mycompItem = [SideBarItem itemWithTitle:_NS("MY COMPUTER") identifier:@"mycomputer"];
228     SideBarItem *devicesItem = [SideBarItem itemWithTitle:_NS("DEVICES") identifier:@"devices"];
229     SideBarItem *lanItem = [SideBarItem itemWithTitle:_NS("LOCAL NETWORK") identifier:@"localnetwork"];
230     SideBarItem *internetItem = [SideBarItem itemWithTitle:_NS("INTERNET") identifier:@"internet"];
231
232     /* SD subnodes, inspired by the Qt4 intf */
233     char **ppsz_longnames;
234     int *p_categories;
235     char **ppsz_names = vlc_sd_GetNames( pl_Get( VLCIntf ), &ppsz_longnames, &p_categories );
236     if (!ppsz_names)
237         msg_Err( VLCIntf, "no sd item found" ); //TODO
238     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
239     int *p_category = p_categories;
240     NSMutableArray *internetItems = [[NSMutableArray alloc] init];
241     NSMutableArray *devicesItems = [[NSMutableArray alloc] init];
242     NSMutableArray *lanItems = [[NSMutableArray alloc] init];
243     NSMutableArray *mycompItems = [[NSMutableArray alloc] init];
244     NSString *o_identifier;
245     for (; *ppsz_name; ppsz_name++, ppsz_longname++, p_category++)
246     {
247         o_identifier = [NSString stringWithCString: *ppsz_name encoding: NSUTF8StringEncoding];
248         o_identifier = [[o_identifier componentsSeparatedByString:@"{"] objectAtIndex:0];
249         switch (*p_category) {
250             case SD_CAT_INTERNET:
251                 {
252                     [internetItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
253                     if (!strncmp( *ppsz_name, "podcast", 7 ))
254                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"film-cast"]];
255                     else
256                         [[internetItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
257                 }
258                 break;
259             case SD_CAT_DEVICES:
260                 {
261                     [devicesItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
262                     [[devicesItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
263                 }
264                 break;
265             case SD_CAT_LAN:
266                 {
267                     [lanItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
268                     [[lanItems lastObject] setIcon: [NSImage imageNamed:@"network-cloud"]];
269                 }
270                 break;
271             case SD_CAT_MYCOMPUTER:
272                 {
273                     [mycompItems addObject: [SideBarItem itemWithTitle: [NSString stringWithCString: *ppsz_longname encoding: NSUTF8StringEncoding] identifier: o_identifier]];
274                     if (!strncmp( *ppsz_name, "video_dir", 9 ))
275                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"film"]];
276                     else if (!strncmp( *ppsz_name, "audio_dir", 9 ))
277                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"music-beam"]];
278                     else if (!strncmp( *ppsz_name, "picture_dir", 11 ))
279                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"picture"]];
280                     else
281                         [[mycompItems lastObject] setIcon: [NSImage imageNamed:@"NSApplicationIcon"]];
282                 }
283                 break;
284             default:
285                 msg_Warn( VLCIntf, "unknown SD type found, skipping (%s)", *ppsz_name );
286                 break;
287         }
288
289         free( *ppsz_name );
290         free( *ppsz_longname );
291     }
292     [mycompItem setChildren: [NSArray arrayWithArray: mycompItems]];
293     [devicesItem setChildren: [NSArray arrayWithArray: devicesItems]];
294     [lanItem setChildren: [NSArray arrayWithArray: lanItems]];
295     [internetItem setChildren: [NSArray arrayWithArray: internetItems]];
296     [mycompItems release];
297     [devicesItems release];
298     [lanItems release];
299     [internetItems release];
300     free( ppsz_names );
301     free( ppsz_longnames );
302     free( p_categories );
303
304     [libraryItem setChildren: [NSArray arrayWithObject: playlistItem]];
305     [o_sidebaritems addObject: libraryItem];
306     if ([mycompItem hasChildren])
307         [o_sidebaritems addObject: mycompItem];
308     if ([devicesItem hasChildren])
309         [o_sidebaritems addObject: devicesItem];
310     if ([lanItem hasChildren])
311         [o_sidebaritems addObject: lanItem];
312     if ([internetItem hasChildren])
313         [o_sidebaritems addObject: internetItem];
314
315     [o_sidebar_view reloadData];
316     [o_sidebar_view selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:YES];
317 }
318
319 #pragma mark -
320 #pragma mark Button Actions
321
322 - (IBAction)play:(id)sender
323 {
324     [[VLCCoreInteraction sharedInstance] play];
325 }
326
327 - (void)resetPreviousButton
328 {
329     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35) {
330         // seems like no further event occured, so let's switch the playback item
331         [[VLCCoreInteraction sharedInstance] previous];
332         just_triggered_previous = NO;
333     }
334 }
335
336 - (void)resetBackwardSkip
337 {
338     // the user stopped skipping, so let's allow him to change the item
339     if (([NSDate timeIntervalSinceReferenceDate] - last_bwd_event) >= 0.35)
340         just_triggered_previous = NO;
341 }
342
343 - (IBAction)bwd:(id)sender
344 {
345     if(!just_triggered_previous)
346     {
347         just_triggered_previous = YES;
348         [self performSelector:@selector(resetPreviousButton)
349                    withObject: NULL
350                    afterDelay:0.40];
351     }
352     else
353     {
354         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.12 )
355         {
356             // we just skipped 3 "continous" events, otherwise we are too fast
357             [[VLCCoreInteraction sharedInstance] backward];
358             last_bwd_event = [NSDate timeIntervalSinceReferenceDate];
359             [self performSelector:@selector(resetBackwardSkip)
360                        withObject: NULL
361                        afterDelay:0.40];
362         }
363     }
364 }
365
366 - (void)resetNextButton
367 {
368     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35) {
369         // seems like no further event occured, so let's switch the playback item
370         [[VLCCoreInteraction sharedInstance] next];
371         just_triggered_next = NO;
372     }
373 }
374
375 - (void)resetForwardSkip
376 {
377     // the user stopped skipping, so let's allow him to change the item
378     if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) >= 0.35)
379         just_triggered_next = NO;
380 }
381
382 - (IBAction)fwd:(id)sender
383 {
384    if(!just_triggered_next)
385     {
386         just_triggered_next = YES;
387         [self performSelector:@selector(resetNextButton)
388                    withObject: NULL
389                    afterDelay:0.40];
390     }
391     else
392     {
393         if (([NSDate timeIntervalSinceReferenceDate] - last_fwd_event) > 0.12 )
394         {
395             // we just skipped 3 "continous" events, otherwise we are too fast
396             [[VLCCoreInteraction sharedInstance] forward];
397             last_fwd_event = [NSDate timeIntervalSinceReferenceDate];
398             [self performSelector:@selector(resetForwardSkip)
399                        withObject: NULL
400                        afterDelay:0.40];
401         }
402     }
403 }
404
405 - (IBAction)stop:(id)sender
406 {
407     [[VLCCoreInteraction sharedInstance] stop];
408 }
409
410 - (IBAction)togglePlaylist:(id)sender
411 {
412     if ([o_video_view isHidden] && [o_playlist_btn isEnabled]) {
413         [o_playlist_table setHidden: YES];
414         [o_video_view setHidden: NO];
415     }
416     else
417     {
418         [o_video_view setHidden: YES];
419         [o_playlist_table setHidden: NO];
420     }
421 }
422
423 - (void)setRepeatOne
424 {
425     [o_repeat_btn setImage: o_repeat_one_img];
426     [o_repeat_btn setAlternateImage: o_repeat_one_pressed_img];   
427 }
428
429 - (void)setRepeatAll
430 {
431     [o_repeat_btn setImage: o_repeat_all_img];
432     [o_repeat_btn setAlternateImage: o_repeat_all_pressed_img];
433 }
434
435 - (void)setRepeatOff
436 {
437     [o_repeat_btn setImage: o_repeat_img];
438     [o_repeat_btn setAlternateImage: o_repeat_pressed_img];
439 }
440
441 - (IBAction)repeat:(id)sender
442 {
443     vlc_value_t looping,repeating;
444     intf_thread_t * p_intf = VLCIntf;
445     playlist_t * p_playlist = pl_Get( p_intf );
446
447     var_Get( p_playlist, "repeat", &repeating );
448     var_Get( p_playlist, "loop", &looping );
449
450     if( !repeating.b_bool && !looping.b_bool )
451     {
452         /* was: no repeating at all, switching to Repeat One */
453         [[VLCCoreInteraction sharedInstance] repeatOne];
454         [self setRepeatOne];
455     }
456     else if( repeating.b_bool && !looping.b_bool )
457     {
458         /* was: Repeat One, switching to Repeat All */
459         [[VLCCoreInteraction sharedInstance] repeatAll];
460         [self setRepeatAll];
461     }
462     else
463     {
464         /* was: Repeat All or bug in VLC, switching to Repeat Off */
465         [[VLCCoreInteraction sharedInstance] repeatOff];
466         [self setRepeatOff];
467     }
468 }
469
470 - (void)setShuffle
471 {
472     bool b_value;
473     playlist_t *p_playlist = pl_Get( VLCIntf );
474     b_value = var_GetBool( p_playlist, "random" );
475         if(b_value) {
476         [o_shuffle_btn setImage: o_shuffle_on_img];
477         [o_shuffle_btn setAlternateImage: o_shuffle_on_pressed_img];
478     }
479     else
480     {
481         [o_shuffle_btn setImage: o_shuffle_img];
482         [o_shuffle_btn setAlternateImage: o_shuffle_pressed_img];
483     }
484 }
485
486 - (IBAction)shuffle:(id)sender
487 {
488     [[VLCCoreInteraction sharedInstance] shuffle];
489     [self setShuffle];
490 }
491
492 - (IBAction)timeSliderAction:(id)sender
493 {
494     float f_updated;
495     input_thread_t * p_input;
496
497     switch( [[NSApp currentEvent] type] )
498     {
499         case NSLeftMouseUp:
500         case NSLeftMouseDown:
501         case NSLeftMouseDragged:
502             f_updated = [sender floatValue];
503             break;
504
505         default:
506             return;
507     }
508     p_input = pl_CurrentInput( VLCIntf );
509     if( p_input != NULL )
510     {
511         vlc_value_t time;
512         vlc_value_t pos;
513         NSString * o_time;
514         char psz_time[MSTRTIME_MAX_SIZE];
515
516         pos.f_float = f_updated / 10000.;
517         var_Set( p_input, "position", pos );
518         [o_time_sld setFloatValue: f_updated];
519
520         var_Get( p_input, "time", &time );
521
522         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
523         if( [o_time_fld timeRemaining] && dur != -1 )
524         {
525             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
526         }
527         else
528             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
529
530         [o_time_fld setStringValue: o_time];
531         [o_fspanel setStreamPos: f_updated andTime: o_time];
532         vlc_object_release( p_input );
533     }
534     [self drawFancyGradientEffectForTimeSlider];
535 }
536
537 - (IBAction)volumeAction:(id)sender
538 {
539     if (sender == o_volume_sld)
540         [[VLCCoreInteraction sharedInstance] setVolume: [sender intValue]];
541     else if (sender == o_volume_down_btn)
542         [[VLCCoreInteraction sharedInstance] mute];
543     else
544         [[VLCCoreInteraction sharedInstance] setVolume: 400];
545 }
546
547 - (IBAction)effects:(id)sender
548 {
549     [[VLCMainMenu sharedInstance] showAudioEffects: sender];
550 }
551
552 - (IBAction)fullscreen:(id)sender
553 {
554     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
555 }
556
557 - (IBAction)dropzoneButtonAction:(id)sender
558 {
559     [[[VLCMain sharedInstance] open] openFileGeneric];
560 }
561
562 #pragma mark -
563 #pragma mark Update interface and respond to foreign events
564 - (void)showDropZone
565 {
566     [o_right_split_view addSubview: o_dropzone_view];
567     [o_dropzone_view setFrame: [o_playlist_table frame]];
568     [[o_playlist_table animator] setHidden:YES];
569 }
570
571 - (void)hideDropZone
572 {
573     [o_dropzone_view removeFromSuperview];
574     [[o_playlist_table animator] setHidden: NO];
575 }
576
577 - (void)updateTimeSlider
578 {
579     input_thread_t * p_input;
580     p_input = pl_CurrentInput( VLCIntf );
581     if( p_input )
582     {
583         vlc_value_t time;
584         NSString * o_time;
585         vlc_value_t pos;
586         char psz_time[MSTRTIME_MAX_SIZE];
587         float f_updated;
588
589         var_Get( p_input, "position", &pos );
590         f_updated = 10000. * pos.f_float;
591         [o_time_sld setFloatValue: f_updated];
592
593         var_Get( p_input, "time", &time );
594
595         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
596         if( [o_time_fld timeRemaining] && dur != -1 )
597         {
598             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
599         }
600         else
601             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
602
603         if (dur == -1) {
604             [o_time_sld setEnabled: NO];
605             [o_time_sld setHidden: YES];
606         } else {
607             [o_time_sld setEnabled: YES];
608             [o_time_sld setHidden: NO];
609         }
610
611         [o_time_fld setStringValue: o_time];
612         [o_time_fld setNeedsDisplay:YES];
613         [o_fspanel setStreamPos: f_updated andTime: o_time];
614         vlc_object_release( p_input );
615     }
616     else
617     {
618         [o_time_sld setFloatValue: 0.0];
619         [o_time_fld setStringValue: @"00:00"];
620         [o_time_sld setEnabled: NO];
621         [o_time_sld setHidden: YES];
622     }
623         
624     [self performSelectorOnMainThread:@selector(drawFancyGradientEffectForTimeSlider) withObject:nil waitUntilDone:NO];
625 }
626
627 - (void)updateVolumeSlider
628 {
629     audio_volume_t i_volume;
630     playlist_t * p_playlist = pl_Get( VLCIntf );
631
632     i_volume = aout_VolumeGet( p_playlist );
633
634     if( i_volume != i_lastShownVolume )
635     {
636         i_lastShownVolume = i_volume;
637         int i_volume_step = 0;
638         i_volume_step = config_GetInt( VLCIntf->p_libvlc, "volume-step" );
639         [o_volume_sld setFloatValue: (float)i_lastShownVolume / i_volume_step];
640         [o_fspanel setVolumeLevel: (float)i_lastShownVolume / i_volume_step];
641     }
642 }
643
644 - (void)updateName
645 {
646     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
647     input_thread_t * p_input;
648     p_input = pl_CurrentInput( VLCIntf );
649     if( p_input )
650     {
651         NSString *aString;
652         char *format = var_InheritString( VLCIntf, "input-title-format" );
653         char *formated = str_format_meta( p_input, format );
654         free( format );
655         aString = [NSString stringWithUTF8String:formated];
656         free( formated );
657
658         char *uri = input_item_GetURI( input_GetItem( p_input ) );
659
660         NSURL * o_url = [NSURL URLWithString: [NSString stringWithUTF8String: uri]];
661         if ([o_url isFileURL])
662             [self setRepresentedURL: o_url];
663         else
664             [self setRepresentedURL: [NSURL URLWithString:@""]];
665         free( uri );
666
667         if ([aString isEqualToString:@""])
668         {
669             if ([o_url isFileURL])
670                 aString = [[NSFileManager defaultManager] displayNameAtPath: [o_url path]];
671             else
672                 aString = [o_url absoluteString];
673         }
674
675         [self setTitle: aString];
676         [o_fspanel setStreamTitle: aString];
677     }
678     else
679     {
680         [self setTitle: _NS("VLC media player")];
681         [self setRepresentedURL: [NSURL URLWithString: @""]];
682     }
683     [o_pool release];
684 }
685
686 - (void)updateWindow
687 {
688     bool b_input = false;
689     bool b_plmul = false;
690     bool b_control = false;
691     bool b_seekable = false;
692     bool b_chapters = false;
693
694     playlist_t * p_playlist = pl_Get( VLCIntf );
695
696     PL_LOCK;
697     b_plmul = playlist_CurrentSize( p_playlist ) > 1;
698     PL_UNLOCK;
699
700     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
701
702     bool b_buffering = NO;
703
704     if( ( b_input = ( p_input != NULL ) ) )
705     {
706         /* seekable streams */
707         cachedInputState = input_GetState( p_input );
708         if ( cachedInputState == INIT_S || cachedInputState == OPENING_S )
709             b_buffering = YES;
710
711         /* seekable streams */
712         b_seekable = var_GetBool( p_input, "can-seek" );
713
714         /* check whether slow/fast motion is possible */
715         b_control = var_GetBool( p_input, "can-rate" );
716
717         /* chapters & titles */
718         //FIXME! b_chapters = p_input->stream.i_area_nb > 1;
719         vlc_object_release( p_input );
720     }
721
722     if( b_buffering )
723     {
724         [o_progress_bar startAnimation:self];
725         [o_progress_bar setIndeterminate:YES];
726         [o_progress_bar setHidden:NO];
727     } else {
728         [o_progress_bar stopAnimation:self];
729         [o_progress_bar setHidden:YES];
730     }
731
732     [o_stop_btn setEnabled: b_input];
733     [o_fwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
734     [o_bwd_btn setEnabled: (b_seekable || b_plmul || b_chapters)];
735     [[VLCMainMenu sharedInstance] setRateControlsEnabled: b_control];
736
737     [o_time_sld setEnabled: b_seekable];
738     [self updateTimeSlider];
739     [o_fspanel setSeekable: b_seekable];
740
741     PL_LOCK;
742     if (playlist_CurrentSize( p_playlist ) >= 1)
743         [self hideDropZone];
744     else
745         [self showDropZone];
746     PL_UNLOCK;
747 }
748
749 - (void)setPause
750 {
751     [o_play_btn setImage: o_pause_img];
752     [o_play_btn setAlternateImage: o_pause_pressed_img];
753     [o_play_btn setToolTip: _NS("Pause")];
754     [o_fspanel setPause];
755 }
756
757 - (void)setPlay
758 {
759     [o_play_btn setImage: o_play_img];
760     [o_play_btn setAlternateImage: o_play_pressed_img];
761     [o_play_btn setToolTip: _NS("Play")];
762     [o_fspanel setPlay];
763 }
764
765 - (void)drawFancyGradientEffectForTimeSlider
766 {
767     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
768     float f_value = ([o_time_sld_middle_view frame].size.width -5) * ([o_time_sld intValue] / [o_time_sld maxValue]);
769     if (f_value > 5.0)
770     {
771         if (f_value != [o_time_sld_fancygradient_view frame].size.width)
772         {
773             [o_time_sld_fancygradient_view setHidden: NO];
774             [o_time_sld_fancygradient_view setFrame: NSMakeRect( [o_time_sld_fancygradient_view frame].origin.x, [o_time_sld_fancygradient_view frame].origin.y, f_value, [o_time_sld_fancygradient_view frame].size.height )];
775             [o_time_sld_fancygradient_view setNeedsDisplay:YES];
776             [o_time_sld_middle_view setNeedsDisplay:YES];
777         }
778     }
779     else
780     {
781         [o_time_sld_fancygradient_view setHidden: YES];
782     }
783     [o_pool release];
784 }
785
786 #pragma mark -
787 #pragma mark Video Output handling
788
789 - (id)videoView
790 {
791     return o_video_view;
792 }
793
794 - (void)setVideoplayEnabled
795 {
796     [o_playlist_btn setEnabled: [[VLCMain sharedInstance] activeVideoPlayback]];
797 }
798
799 - (void)resizeWindow
800 {
801     if ( !b_fullscreen )
802     {
803         NSPoint topleftbase;
804         NSPoint topleftscreen;
805         NSRect new_frame;
806         topleftbase.x = 0;
807         topleftbase.y = [self frame].size.height;
808         topleftscreen = [self convertBaseToScreen: topleftbase];
809
810         /* Calculate the window's new size */
811         new_frame.size.width = [self frame].size.width - [o_video_view frame].size.width + nativeVideoSize.width;
812         new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + nativeVideoSize.height;
813
814         new_frame.origin.x = topleftscreen.x;
815         new_frame.origin.y = topleftscreen.y - new_frame.size.height;
816
817         [[self animator] setFrame:new_frame display:YES];
818     }
819 }
820
821 - (void)setNativeVideoSize:(NSSize)size
822 {
823     if (size.width != nativeVideoSize.width || size.height != nativeVideoSize.height )
824     {
825         nativeVideoSize = size;
826         [self resizeWindow];
827     }
828 }
829
830 #pragma mark -
831 #pragma mark Fullscreen support
832 - (void)showFullscreenController
833 {
834     if (b_fullscreen)
835         [o_fspanel fadeIn];
836 }
837
838 - (BOOL)isFullscreen
839 {
840     return b_fullscreen;
841 }
842
843 - (void)lockFullscreenAnimation
844 {
845     [o_animation_lock lock];
846 }
847
848 - (void)unlockFullscreenAnimation
849 {
850     [o_animation_lock unlock];
851 }
852
853 - (void)enterFullscreen
854 {
855     NSMutableDictionary *dict1, *dict2;
856     NSScreen *screen;
857     NSRect screen_rect;
858     NSRect rect;
859     vout_thread_t *p_vout = getVout();
860     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
861
862     if( p_vout )
863         screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_GetInteger( p_vout, "video-device" )];
864
865     [self lockFullscreenAnimation];
866
867     if (!screen)
868     {
869         msg_Dbg( VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode" );
870         screen = [self screen];
871     }
872     if (!screen)
873     {
874         msg_Dbg( VLCIntf, "Using deepest screen" );
875         screen = [NSScreen deepestScreen];
876     }
877
878     if( p_vout )
879         vlc_object_release( p_vout );
880
881     screen_rect = [screen frame];
882
883     [o_fullscreen_btn setState: YES];
884
885     [NSCursor setHiddenUntilMouseMoves: YES];
886
887     if( blackout_other_displays )
888         [screen blackoutOtherScreens];
889
890     /* Make sure we don't see the window flashes in float-on-top mode */
891     i_originalLevel = [self level];
892     [self setLevel:NSNormalWindowLevel];
893
894     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
895     if (!o_fullscreen_window)
896     {
897         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
898
899         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
900         rect.origin.x += [self frame].origin.x;
901         rect.origin.y += [self frame].origin.y;
902         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
903         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
904         [o_fullscreen_window setCanBecomeKeyWindow: YES];
905
906         if (![self isVisible] || [self alphaValue] == 0.0)
907         {
908             /* We don't animate if we are not visible, instead we
909              * simply fade the display */
910             CGDisplayFadeReservationToken token;
911
912             if( blackout_other_displays )
913             {
914                 CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
915                 CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
916             }
917
918             if ([screen isMainScreen])
919                 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
920
921             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
922             [o_temp_view setFrame:[o_video_view frame]];
923             [o_fullscreen_window setContentView:o_video_view];
924
925             [o_fullscreen_window makeKeyAndOrderFront:self];
926             [o_fullscreen_window orderFront:self animate:YES];
927
928             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
929             [o_fullscreen_window setLevel:NSNormalWindowLevel];
930
931             if( blackout_other_displays )
932             {
933                 CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
934                 CGReleaseDisplayFadeReservation( token );
935             }
936
937             /* Will release the lock */
938             [self hasBecomeFullscreen];
939
940             return;
941         }
942
943         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
944         NSDisableScreenUpdates();
945         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
946         [o_temp_view setFrame:[o_video_view frame]];
947         [o_fullscreen_window setContentView:o_video_view];
948         [o_fullscreen_window makeKeyAndOrderFront:self];
949         NSEnableScreenUpdates();
950     }
951
952     /* We are in fullscreen (and no animation is running) */
953     if (b_fullscreen)
954     {
955         /* Make sure we are hidden */
956         [super orderOut: self];
957         [self unlockFullscreenAnimation];
958         return;
959     }
960
961     if (o_fullscreen_anim1)
962     {
963         [o_fullscreen_anim1 stopAnimation];
964         [o_fullscreen_anim1 release];
965     }
966     if (o_fullscreen_anim2)
967     {
968         [o_fullscreen_anim2 stopAnimation];
969         [o_fullscreen_anim2 release];
970     }
971
972     if ([screen isMainScreen])
973         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
974
975     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
976     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
977
978     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
979     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
980
981     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
982     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
983     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
984
985     /* Strategy with NSAnimation allocation:
986      - Keep at most 2 animation at a time
987      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
988      */
989     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
990     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
991
992     [dict1 release];
993     [dict2 release];
994
995     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
996     [o_fullscreen_anim1 setDuration: 0.3];
997     [o_fullscreen_anim1 setFrameRate: 30];
998     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
999     [o_fullscreen_anim2 setDuration: 0.2];
1000     [o_fullscreen_anim2 setFrameRate: 30];
1001
1002     [o_fullscreen_anim2 setDelegate: self];
1003     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1004
1005     [o_fullscreen_anim1 startAnimation];
1006     /* fullscreenAnimation will be unlocked when animation ends */
1007 }
1008
1009 - (void)hasBecomeFullscreen
1010 {
1011     [o_fullscreen_window makeFirstResponder: o_video_view];
1012
1013     [o_fullscreen_window makeKeyWindow];
1014     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
1015
1016     /* tell the fspanel to move itself to front next time it's triggered */
1017     [o_fspanel setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
1018     [o_fspanel setActive: nil];
1019
1020     if([self isVisible])
1021         [super orderOut: self];
1022
1023     [o_fspanel setActive: nil];
1024
1025     b_fullscreen = YES;
1026     [self unlockFullscreenAnimation];
1027 }
1028
1029 - (void)leaveFullscreen
1030 {
1031     [self leaveFullscreenAndFadeOut: NO];
1032 }
1033
1034 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1035 {
1036     NSMutableDictionary *dict1, *dict2;
1037     NSRect frame;
1038     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
1039
1040     [self lockFullscreenAnimation];
1041
1042     b_fullscreen = NO;
1043     [o_fullscreen_btn setState: NO];
1044
1045     /* We always try to do so */
1046     [NSScreen unblackoutScreens];
1047     vout_thread_t *p_vout = getVout();
1048     if (p_vout)
1049     {
1050         if( var_GetBool( p_vout, "video-on-top" ) )
1051             [self setLevel: NSStatusWindowLevel];
1052         else
1053             [self setLevel: NSNormalWindowLevel];
1054         vlc_object_release( p_vout );
1055     }
1056     [self makeKeyAndOrderFront: nil];
1057
1058     /* Don't do anything if o_fullscreen_window is already closed */
1059     if (!o_fullscreen_window)
1060     {
1061         [self unlockFullscreenAnimation];
1062         return;
1063     }
1064
1065     if (fadeout)
1066     {
1067         /* We don't animate if we are not visible, instead we
1068          * simply fade the display */
1069         CGDisplayFadeReservationToken token;
1070
1071         if( blackout_other_displays )
1072         {
1073             CGAcquireDisplayFadeReservation( kCGMaxDisplayReservationInterval, &token );
1074             CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
1075         }
1076
1077         [o_fspanel setNonActive: nil];
1078         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1079
1080         /* Will release the lock */
1081         [self hasEndedFullscreen];
1082
1083         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1084          * here */
1085         b_window_is_invisible = YES;
1086
1087         if( blackout_other_displays )
1088         {
1089             CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
1090             CGReleaseDisplayFadeReservation( token );
1091         }
1092
1093         return;
1094     }
1095
1096     [self setAlphaValue: 0.0];
1097     [self orderFront: self];
1098
1099     [o_fspanel setNonActive: nil];
1100     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
1101
1102     if (o_fullscreen_anim1)
1103     {
1104         [o_fullscreen_anim1 stopAnimation];
1105         [o_fullscreen_anim1 release];
1106     }
1107     if (o_fullscreen_anim2)
1108     {
1109         [o_fullscreen_anim2 stopAnimation];
1110         [o_fullscreen_anim2 release];
1111     }
1112
1113     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1114     frame.origin.x += [self frame].origin.x;
1115     frame.origin.y += [self frame].origin.y;
1116
1117     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1118     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
1119     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1120
1121     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
1122     [dict2 release];
1123
1124     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1125     [o_fullscreen_anim2 setDuration: 0.3];
1126     [o_fullscreen_anim2 setFrameRate: 30];
1127
1128     [o_fullscreen_anim2 setDelegate: self];
1129
1130     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1131
1132     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1133     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1134     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1135
1136     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
1137     [dict1 release];
1138
1139     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1140     [o_fullscreen_anim1 setDuration: 0.2];
1141     [o_fullscreen_anim1 setFrameRate: 30];
1142     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1143
1144     /* Make sure o_fullscreen_window is the frontmost window */
1145     [o_fullscreen_window orderFront: self];
1146
1147     [o_fullscreen_anim1 startAnimation];
1148     /* fullscreenAnimation will be unlocked when animation ends */
1149 }
1150
1151 - (void)hasEndedFullscreen
1152 {
1153     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1154     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1155     NSDisableScreenUpdates();
1156     [o_video_view retain];
1157     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1158     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1159     [o_video_view release];
1160     [o_video_view setFrame:[o_temp_view frame]];
1161     [self makeFirstResponder: o_video_view];
1162     if ([self isVisible])
1163         [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
1164     [o_fullscreen_window orderOut: self];
1165     NSEnableScreenUpdates();
1166
1167     [o_fullscreen_window release];
1168     o_fullscreen_window = nil;
1169     [self setLevel:i_originalLevel];
1170
1171     [self unlockFullscreenAnimation];
1172 }
1173
1174 - (void)animationDidEnd:(NSAnimation*)animation
1175 {
1176     NSArray *viewAnimations;
1177     if( o_makekey_anim == animation )
1178     {
1179         [o_makekey_anim release];
1180         return;
1181     }
1182     if ([animation currentValue] < 1.0)
1183         return;
1184
1185     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1186     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1187     if ([viewAnimations count] >=1 &&
1188         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
1189     {
1190         /* Fullscreen ended */
1191         [self hasEndedFullscreen];
1192     }
1193     else
1194     {
1195         /* Fullscreen started */
1196         [self hasBecomeFullscreen];
1197     }
1198 }
1199
1200 - (void)orderOut: (id)sender
1201 {
1202     [super orderOut: sender];
1203
1204     /* Make sure we leave fullscreen */
1205     [self leaveFullscreenAndFadeOut: YES];
1206 }
1207
1208 - (void)makeKeyAndOrderFront: (id)sender
1209 {
1210     /* Hack
1211      * when we exit fullscreen and fade out, we may endup in
1212      * having a window that is faded. We can't have it fade in unless we
1213      * animate again. */
1214
1215     if(!b_window_is_invisible)
1216     {
1217         /* Make sure we don't do it too much */
1218         [super makeKeyAndOrderFront: sender];
1219         return;
1220     }
1221
1222     [super setAlphaValue:0.0f];
1223     [super makeKeyAndOrderFront: sender];
1224
1225     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1226     [dict setObject:self forKey:NSViewAnimationTargetKey];
1227     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1228
1229     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1230     [dict release];
1231
1232     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1233     [o_makekey_anim setDuration: 0.1];
1234     [o_makekey_anim setFrameRate: 30];
1235     [o_makekey_anim setDelegate: self];
1236
1237     [o_makekey_anim startAnimation];
1238     b_window_is_invisible = NO;
1239
1240     /* fullscreenAnimation will be unlocked when animation ends */
1241 }
1242
1243
1244
1245 /* Make sure setFrame gets executed on main thread especially if we are animating.
1246  * (Thus we won't block the video output thread) */
1247 - (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate
1248 {
1249     struct { NSRect frame; BOOL display; BOOL animate;} args;
1250     NSData *packedargs;
1251
1252     args.frame = frame;
1253     args.display = display;
1254     args.animate = animate;
1255
1256     packedargs = [NSData dataWithBytes:&args length:sizeof(args)];
1257
1258     [self performSelectorOnMainThread:@selector(setFrameOnMainThread:)
1259                            withObject: packedargs waitUntilDone: YES];
1260 }
1261
1262 - (void)setFrameOnMainThread:(NSData*)packedargs
1263 {
1264     struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes];
1265
1266     if( args->animate )
1267     {
1268         /* Make sure we don't block too long and set up a non blocking animation */
1269         NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
1270                                self, NSViewAnimationTargetKey,
1271                                [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey,
1272                                [NSValue valueWithRect:args->frame], NSViewAnimationEndFrameKey, nil];
1273
1274         NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1275         [dict release];
1276
1277         [anim setAnimationBlockingMode: NSAnimationNonblocking];
1278         [anim setDuration: 0.4];
1279         [anim setFrameRate: 30];
1280         [anim startAnimation];
1281     }
1282     else {
1283         [super setFrame:args->frame display:args->display animate:args->animate];
1284     }
1285 }
1286
1287 #pragma mark -
1288 #pragma mark Side Bar Data handling
1289 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1290 - (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item
1291 {
1292         //Works the same way as the NSOutlineView data source: `nil` means a parent item
1293         if(item==nil) {
1294                 return [o_sidebaritems count];
1295         }
1296         else {
1297                 return [[item children] count];
1298         }
1299 }
1300
1301
1302 - (id)sourceList:(PXSourceList*)aSourceList child:(NSUInteger)index ofItem:(id)item
1303 {
1304     //Works the same way as the NSOutlineView data source: `nil` means a parent item
1305         if(item==nil) {
1306                 return [o_sidebaritems objectAtIndex:index];
1307         }
1308         else {
1309                 return [[item children] objectAtIndex:index];
1310         }
1311 }
1312
1313
1314 - (id)sourceList:(PXSourceList*)aSourceList objectValueForItem:(id)item
1315 {
1316         return [item title];
1317 }
1318
1319 - (void)sourceList:(PXSourceList*)aSourceList setObjectValue:(id)object forItem:(id)item
1320 {
1321         [item setTitle:object];
1322 }
1323
1324 - (BOOL)sourceList:(PXSourceList*)aSourceList isItemExpandable:(id)item
1325 {
1326         return [item hasChildren];
1327 }
1328
1329
1330 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasBadge:(id)item
1331 {
1332         return [item hasBadge];
1333 }
1334
1335
1336 - (NSInteger)sourceList:(PXSourceList*)aSourceList badgeValueForItem:(id)item
1337 {
1338         return [item badgeValue];
1339 }
1340
1341
1342 - (BOOL)sourceList:(PXSourceList*)aSourceList itemHasIcon:(id)item
1343 {
1344         return [item hasIcon];
1345 }
1346
1347
1348 - (NSImage*)sourceList:(PXSourceList*)aSourceList iconForItem:(id)item
1349 {
1350         return [item icon];
1351 }
1352
1353 - (NSMenu*)sourceList:(PXSourceList*)aSourceList menuForEvent:(NSEvent*)theEvent item:(id)item
1354 {
1355         if ([theEvent type] == NSRightMouseDown || ([theEvent type] == NSLeftMouseDown && ([theEvent modifierFlags] & NSControlKeyMask) == NSControlKeyMask)) {
1356                 NSMenu * m = [[NSMenu alloc] init];
1357                 if (item != nil)
1358                         [m addItemWithTitle:[item title] action:nil keyEquivalent:@""];
1359                 return [m autorelease];
1360         }
1361         return nil;
1362 }
1363
1364 #pragma mark -
1365 #pragma mark Side Bar Delegate Methods
1366 /* taken under BSD-new from the PXSourceList sample project, adapted for VLC */
1367 - (BOOL)sourceList:(PXSourceList*)aSourceList isGroupAlwaysExpanded:(id)group
1368 {
1369         if([[group identifier] isEqualToString:@"library"])
1370                 return YES;
1371
1372         return NO;
1373 }
1374
1375 - (void)sourceListSelectionDidChange:(NSNotification *)notification
1376 {
1377         NSIndexSet *selectedIndexes = [o_sidebar_view selectedRowIndexes];
1378
1379         //Set the label text to represent the new selection
1380     if([selectedIndexes count]==1) {
1381                 NSString *title = [[o_sidebar_view itemAtRow:[selectedIndexes firstIndex]] title];
1382
1383                 [o_chosen_category_lbl setStringValue:title];
1384         }
1385         else {
1386                 [o_chosen_category_lbl setStringValue:@"(none)"];
1387         }
1388 }
1389
1390 @end
1391
1392 @implementation VLCProgressBarGradientEffect
1393 - (void)dealloc
1394 {
1395     [o_time_sld_gradient_left_img release];
1396     [o_time_sld_gradient_middle_img release];
1397     [o_time_sld_gradient_right_img release];
1398     [super dealloc];
1399 }
1400
1401 - (void)loadImagesInDarkStyle: (BOOL)b_value
1402 {
1403     if (b_value)
1404     {
1405         o_time_sld_gradient_left_img = [[NSImage imageNamed:@"progressbar-fill-left_dark"] retain];
1406         o_time_sld_gradient_middle_img = [[NSImage imageNamed:@"progressbar-fill-middle_dark"] retain];
1407         o_time_sld_gradient_right_img = [[NSImage imageNamed:@"progressbar-fill-right_dark"] retain];
1408     }
1409     else
1410     {
1411         o_time_sld_gradient_left_img = [[NSImage imageNamed:@"progression-fill-left"] retain];
1412         o_time_sld_gradient_middle_img = [[NSImage imageNamed:@"progression-fill-middle"] retain];
1413         o_time_sld_gradient_right_img = [[NSImage imageNamed:@"progression-fill-right"] retain];
1414     }
1415 }
1416
1417 - (void)drawRect:(NSRect)rect
1418 {
1419     NSRect bnds = [self bounds];
1420     NSDrawThreePartImage( bnds, o_time_sld_gradient_left_img, o_time_sld_gradient_middle_img, o_time_sld_gradient_right_img, NO, NSCompositeSourceOver, 1, NO );
1421 }
1422 @end