]> git.sesse.net Git - vlc/blob - modules/gui/macosx/embeddedwindow.m
macosx: merge Eric Dudiak's code from GSoC 2008
[vlc] / modules / gui / macosx / embeddedwindow.m
1 /*****************************************************************************
2  * embeddedwindow.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2005-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Benjamin Pracht <bigben at videolan dot org>
8  *          Felix Paul Kühne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 /* DisableScreenUpdates, SetSystemUIMode, ... */
30 #import <QuickTime/QuickTime.h>
31
32 #import "intf.h"
33 #import "controls.h"
34 #import "vout.h"
35 #import "embeddedwindow.h"
36 #import "fspanel.h"
37 #import "playlist.h"
38
39 /*****************************************************************************
40  * extension to NSWindow's interface to fix compilation warnings
41  * and let us access this functions properly
42  * this uses a private Apple-API, but works fine on all current OSX releases
43  * keep checking for compatiblity with future releases though
44  *****************************************************************************/
45
46 @interface NSWindow (UndocumentedWindowProperties)
47 - (void)setBottomCornerRounded: (BOOL)value;
48 @end
49
50 /*****************************************************************************
51  * VLCEmbeddedWindow Implementation
52  *****************************************************************************/
53
54 @implementation VLCEmbeddedWindow
55
56 - (void)awakeFromNib
57 {
58     [self setDelegate: self];
59     [self setBottomCornerRounded:NO];
60
61     /* button strings */
62     [o_btn_backward setToolTip: _NS("Rewind")];
63     [o_btn_forward setToolTip: _NS("Fast Forward")];
64     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
65     [o_btn_play setToolTip: _NS("Play")];
66     [o_timeslider setToolTip: _NS("Position")];
67     [o_btn_prev setToolTip: _NS("Previous")];
68     [o_btn_stop setToolTip: _NS("Stop")];
69     [o_btn_next setToolTip: _NS("Next")];
70     [o_volumeslider setToolTip: _NS("Volume")];
71     [o_btn_playlist setToolTip: _NS("Playlist")];
72     [self setTitle: _NS("VLC media player")];
73
74     if(MACOS_VERSION < 10.5f) {
75         o_img_play = [NSImage imageNamed: @"play"];
76         o_img_pause = [NSImage imageNamed: @"pause"];
77         [o_btn_play setImage: [NSImage imageNamed: @"play"]];
78     }
79     else {
80         o_img_play = [NSImage imageNamed: @"play_big"];
81         o_img_pause = [NSImage imageNamed: @"pause_big"];
82     }
83     [self controlTintChanged];
84     [[NSNotificationCenter defaultCenter] addObserver: self
85                                              selector: @selector( controlTintChanged )
86                                                  name: NSControlTintDidChangeNotification
87                                                object: nil];
88
89     /* Set color of sidebar to Leopard's "Sidebar Blue" */
90     [o_sidebar_list setBackgroundColor: [NSColor colorWithCalibratedRed:0.820
91                                                                   green:0.843
92                                                                    blue:0.886
93                                                                   alpha:1.0]];
94     
95     [self setMinSize:NSMakeSize([o_sidebar_list convertRect:[o_sidebar_list bounds]
96                                                      toView: nil].size.width + 551., 114.)];
97
98     /* Useful to save o_view frame in fullscreen mode */
99     o_temp_view = [[NSView alloc] init];
100     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
101
102     o_fullscreen_window = nil;
103     o_fullscreen_anim1 = o_fullscreen_anim2 = nil;
104
105     /* Not fullscreen when we wake up */
106     [o_btn_fullscreen setState: NO];
107     b_fullscreen = NO;
108
109     /* Make sure setVisible: returns NO */
110     [self orderOut:self];
111     //b_window_is_invisible = YES;
112     videoRatio = NSMakeSize( 0., 0. );
113 }
114
115 - (void)controlTintChanged
116 {
117     BOOL b_playing = NO;
118     if( [o_btn_play alternateImage] == o_img_play_pressed )
119         b_playing = YES;
120     
121     if (MACOS_VERSION < 10.5f) {
122         /* System is running Tiger and should use aqua buttons */
123         [o_btn_backward setImage: [NSImage imageNamed: @"skip_previous_active"]];
124         [o_btn_forward setImage: [NSImage imageNamed: @"skip_forward_active"]];
125         if( [NSColor currentControlTint] == NSGraphiteControlTint )
126         {
127             o_img_play_pressed = [NSImage imageNamed: @"play_graphite"];
128             o_img_pause_pressed = [NSImage imageNamed: @"pause_graphite"];
129             [o_btn_backward setAlternateImage: [NSImage imageNamed: @"skip_previous_graphite"]];
130             [o_btn_forward setAlternateImage: [NSImage imageNamed: @"skip_forward_graphite"]];
131         }
132         else
133         {
134             o_img_play_pressed = [NSImage imageNamed: @"play_blue"];
135             o_img_pause_pressed = [NSImage imageNamed: @"pause_blue"];
136             [o_btn_backward setAlternateImage: [NSImage imageNamed: @"skip_previous_blue"]];
137             [o_btn_forward setAlternateImage: [NSImage imageNamed: @"skip_forward_blue"]];
138         }
139     }
140     else{
141         /* System is running Leopard or later and should use metal buttons */
142         o_img_play_pressed = [NSImage imageNamed: @"play_big_down"];
143         o_img_pause_pressed = [NSImage imageNamed: @"pause_big_down"];
144     }
145     
146     if( b_playing )
147         [o_btn_play setAlternateImage: o_img_play_pressed];
148     else
149         [o_btn_play setAlternateImage: o_img_pause_pressed];
150 }
151
152 - (void)dealloc
153 {
154     [[NSNotificationCenter defaultCenter] removeObserver: self];
155     [o_img_play release];
156     [o_img_play_pressed release];
157     [o_img_pause release];
158     [o_img_pause_pressed release];
159     
160     [super dealloc];
161 }
162
163 - (void)setTime:(NSString *)o_arg_time position:(float)f_position
164 {
165     [o_time setStringValue: o_arg_time];
166     [o_timeslider setFloatValue: f_position];
167 }
168
169 - (void)playStatusUpdated:(int)i_status
170 {
171     if( i_status == PLAYING_S )
172     {
173         [o_btn_play setImage: o_img_pause];
174         [o_btn_play setAlternateImage: o_img_pause_pressed];
175         [o_btn_play setToolTip: _NS("Pause")];
176     }
177     else
178     {
179         [o_btn_play setImage: o_img_play];
180         [o_btn_play setAlternateImage: o_img_play_pressed];
181         [o_btn_play setToolTip: _NS("Play")];
182     }
183 }
184
185 - (void)setSeekable:(BOOL)b_seekable
186 {
187     [o_btn_forward setEnabled: b_seekable];
188     [o_btn_backward setEnabled: b_seekable];
189     [o_timeslider setEnabled: b_seekable];
190 }
191
192 - (void)setScrollString:(NSString *)o_string
193 {
194     [o_scrollfield setStringValue: o_string];
195 }
196
197 - (id)getPgbar
198 {
199     if( o_main_pgbar )
200         return o_main_pgbar;
201     
202     return nil;
203 }
204
205 - (void)setStop:(BOOL)b_input
206 {
207     [o_btn_stop setEnabled: b_input];
208 }
209
210 - (void)setNext:(BOOL)b_input
211 {
212     [o_btn_next setEnabled: b_input];
213 }
214
215 - (void)setPrev:(BOOL)b_input
216 {
217     [o_btn_prev setEnabled: b_input];
218 }
219
220 - (void)setVolumeEnabled:(BOOL)b_input
221 {
222     [o_volumeslider setEnabled: b_input];
223 }
224
225 - (void)setVolumeSlider:(float)f_level
226 {
227     [o_volumeslider setFloatValue: f_level];
228 }
229
230 - (BOOL)windowShouldZoom:(NSWindow *)sender toFrame:(NSRect)newFrame
231 {
232     [self setFrame: newFrame display: YES animate: YES];
233     return NO;
234 }
235
236 - (BOOL)windowShouldClose:(id)sender
237 {
238     playlist_t * p_playlist = pl_Hold( VLCIntf );
239
240     /* Only want to stop playback if video is playing */
241     if( videoRatio.height != 0. && videoRatio.width != 0. )
242         playlist_Stop( p_playlist );
243     pl_Release( VLCIntf );
244     return YES;
245 }
246
247 - (NSView *)mainView
248 {
249     if (o_fullscreen_window)
250         return o_temp_view;
251     else
252         return o_view;
253 }
254
255 - (void)setVideoRatio:(NSSize)ratio
256 {
257     videoRatio = ratio;
258 }
259
260 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
261 {
262         NSView *playlist_area = [[o_vertical_split subviews] objectAtIndex:1];
263         NSRect newList = [playlist_area frame];
264         if( newList.size.height < 50 && newList.size.height > 0 ) {
265                 [self togglePlaylist:self];
266         }
267     
268     /* With no video open or with the playlist open the behavior is odd */    
269     if( newList.size.height > 50 )
270         return proposedFrameSize;
271         
272     if( videoRatio.height == 0. || videoRatio.width == 0. )
273         return proposedFrameSize;
274
275     NSRect viewRect = [o_view convertRect:[o_view bounds] toView: nil];
276     NSRect contentRect = [self contentRectForFrameRect:[self frame]];
277     float marginy = viewRect.origin.y + [self frame].size.height - contentRect.size.height;
278     float marginx = contentRect.size.width - viewRect.size.width;
279
280     proposedFrameSize.height = (proposedFrameSize.width - marginx) * videoRatio.height / videoRatio.width + marginy;
281
282     return proposedFrameSize;
283 }
284
285 - (void)becomeMainWindow
286 {
287     [o_sidebar_list setBackgroundColor: [NSColor colorWithCalibratedRed:0.820
288                                                                   green:0.843
289                                                                    blue:0.886
290                                                                   alpha:1.0]];
291         [o_status becomeMainWindow];
292     [super becomeMainWindow];
293 }
294
295 - (void)resignMainWindow
296 {
297     [o_sidebar_list setBackgroundColor: [NSColor colorWithCalibratedWhite:0.91 alpha:1.0]];
298         [o_status resignMainWindow];
299     [super resignMainWindow];
300 }
301
302 - (float)splitView:(NSSplitView *) splitView constrainSplitPosition:(float) proposedPosition ofSubviewAt:(int) index
303 {
304         if([splitView isVertical])
305                 return proposedPosition;
306         else {
307                 float bottom = [splitView frame].size.height - [splitView dividerThickness];
308                 if(proposedPosition > bottom - 50) {
309                         [o_btn_playlist setState: NSOffState];
310                         [o_searchfield setHidden:YES];
311                         [o_playlist_view setHidden:YES];
312                         return bottom;
313                 }
314                 else {
315                         [o_btn_playlist setState: NSOnState];
316                         [o_searchfield setHidden:NO];
317                         [o_playlist_view setHidden:NO];
318                         [o_playlist swapPlaylists: o_playlist_table];
319                         [o_vlc_main togglePlaylist:self];
320                         return proposedPosition;
321                 }
322         }
323 }
324
325 - (void)splitViewWillResizeSubviews:(NSNotification *) notification
326 {
327
328 }
329
330 - (float)splitView:(NSSplitView *) splitView constrainMinCoordinate:(float) proposedMin ofSubviewAt:(int) offset
331 {
332         if([splitView isVertical])
333                 return 125.;
334         else
335                 return 0.;
336 }
337
338 - (float)splitView:(NSSplitView *) splitView constrainMaxCoordinate:(float) proposedMax ofSubviewAt:(int) offset
339 {
340     if([splitView isVertical])
341                 return MIN([self frame].size.width - 551, 300);
342         else
343                 return [splitView frame].size.height;
344 }
345
346 - (BOOL)splitView:(NSSplitView *) splitView canCollapseSubview:(NSView *) subview
347 {
348         if([splitView isVertical])
349                 return NO;
350         else
351                 return NO;
352 }
353
354 - (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect
355    ofDividerAtIndex:(NSInteger)dividerIndex
356 {
357         if([splitView isVertical]) {
358                 drawnRect.origin.x -= 3;
359                 drawnRect.size.width += 5;
360                 return drawnRect;
361         }
362         else
363                 return drawnRect;
364 }
365
366 - (IBAction)togglePlaylist:(id)sender
367 {
368         NSView *playback_area = [[o_vertical_split subviews] objectAtIndex:0];
369         NSView *playlist_area = [[o_vertical_split subviews] objectAtIndex:1];
370         NSRect newVid = [playback_area frame];
371         NSRect newList = [playlist_area frame];
372         if(newList.size.height < 50 && sender != self && sender != o_vlc_main) {
373                 newList.size.height = newVid.size.height/2;
374                 newVid.size.height = newVid.size.height/2;
375                 newVid.origin.y = newVid.origin.y + newList.size.height;
376                 [o_btn_playlist setState: NSOnState];
377                 [o_searchfield setHidden:NO];
378                 [o_playlist_view setHidden:NO];
379                 [o_playlist swapPlaylists: o_playlist_table];
380                 [o_vlc_main togglePlaylist:self];
381         }
382         else {
383                 newVid.size.height = newVid.size.height + newList.size.height;
384                 newList.size.height = 0;
385                 newVid.origin.y = 0;
386                 [o_btn_playlist setState: NSOffState];
387                 [o_searchfield setHidden:YES];
388                 [o_playlist_view setHidden:YES];
389         }
390         [playback_area setFrame: newVid];
391         [playlist_area setFrame: newList];
392 }
393
394 /*****************************************************************************
395  * Fullscreen support
396  */
397
398 - (BOOL)isFullscreen
399 {
400     return b_fullscreen;
401 }
402
403 - (void)lockFullscreenAnimation
404 {
405     [o_animation_lock lock];
406 }
407
408 - (void)unlockFullscreenAnimation
409 {
410     [o_animation_lock unlock];
411 }
412
413 - (void)enterFullscreen
414 {
415     NSMutableDictionary *dict1, *dict2;
416     NSScreen *screen;
417     NSRect screen_rect;
418     NSRect rect;
419     vout_thread_t *p_vout = vlc_object_find( VLCIntf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
420     BOOL blackout_other_displays = config_GetInt( VLCIntf, "macosx-black" );
421
422     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_GetInteger( p_vout, "video-device" )]; 
423  
424     [self lockFullscreenAnimation];
425
426     if (!screen)
427     {
428         msg_Dbg( p_vout, "chosen screen isn't present, using current screen for fullscreen mode" );
429         screen = [self screen];
430     }
431     if (!screen)
432     {
433         msg_Dbg( p_vout, "Using deepest screen" );
434         screen = [NSScreen deepestScreen];
435     }
436
437     vlc_object_release( p_vout );
438
439     screen_rect = [screen frame];
440
441     [o_btn_fullscreen setState: YES];
442
443     [NSCursor setHiddenUntilMouseMoves: YES];
444  
445     if( blackout_other_displays )        
446         [screen blackoutOtherScreens];
447
448     /* Make sure we don't see the window flashes in float-on-top mode */
449     originalLevel = [self level];
450     [self setLevel:NSNormalWindowLevel];
451
452     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
453     if (!o_fullscreen_window)
454     {
455         /* We can't change the styleMask of an already created NSWindow, so we create an other window, and do eye catching stuff */
456
457         rect = [[o_view superview] convertRect: [o_view frame] toView: nil]; /* Convert to Window base coord */
458         rect.origin.x += [self frame].origin.x;
459         rect.origin.y += [self frame].origin.y;
460         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
461         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
462         [o_fullscreen_window setCanBecomeKeyWindow: YES];
463
464         if (![self isVisible] || [self alphaValue] == 0.0 || MACOS_VERSION < 10.4f)
465         {
466             /* We don't animate if we are not visible or if we are running on
467              * Mac OS X <10.4 which doesn't support NSAnimation, instead we
468              * simply fade the display */
469             CGDisplayFadeReservationToken token;
470  
471             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
472             CGDisplayFade( token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
473  
474             if ([screen isMainScreen])
475                 SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
476  
477             [[self contentView] replaceSubview:o_view with:o_temp_view];
478             [o_temp_view setFrame:[o_view frame]];
479             [o_fullscreen_window setContentView:o_view];
480
481             [o_fullscreen_window makeKeyAndOrderFront:self];
482
483             [o_fullscreen_window makeKeyAndOrderFront:self];
484             [o_fullscreen_window orderFront:self animate:YES];
485
486             [o_fullscreen_window setFrame:screen_rect display:YES];
487
488             CGDisplayFade( token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
489             CGReleaseDisplayFadeReservation( token);
490
491             /* Will release the lock */
492             [self hasBecomeFullscreen];
493
494             return;
495         }
496  
497         /* Make sure we don't see the o_view disappearing of the screen during this operation */
498         DisableScreenUpdates();
499         [[self contentView] replaceSubview:o_view with:o_temp_view];
500         [o_temp_view setFrame:[o_view frame]];
501         [o_fullscreen_window setContentView:o_view];
502         [o_fullscreen_window makeKeyAndOrderFront:self];
503         EnableScreenUpdates();
504     }
505
506     if (MACOS_VERSION < 10.4f)
507     {
508         /* We were already fullscreen nothing to do when NSAnimation
509          * is not supported */
510         [self unlockFullscreenAnimation];
511         return;
512     }
513
514     /* We are in fullscreen (and no animation is running) */
515     if (b_fullscreen)
516     {
517         /* Make sure we are hidden */
518         [super orderOut: self];
519         [self unlockFullscreenAnimation];
520         return;
521     }
522
523     if (o_fullscreen_anim1)
524     {
525         [o_fullscreen_anim1 stopAnimation];
526         [o_fullscreen_anim1 release];
527     }
528     if (o_fullscreen_anim2)
529     {
530         [o_fullscreen_anim2 stopAnimation];
531         [o_fullscreen_anim2 release];
532     }
533  
534     if ([screen isMainScreen])
535         SetSystemUIMode( kUIModeAllHidden, kUIOptionAutoShowMenuBar);
536
537     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
538     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
539
540     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
541     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
542
543     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
544     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
545     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
546
547     /* Strategy with NSAnimation allocation:
548         - Keep at most 2 animation at a time
549         - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
550     */
551     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
552     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
553
554     [dict1 release];
555     [dict2 release];
556
557     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
558     [o_fullscreen_anim1 setDuration: 0.3];
559     [o_fullscreen_anim1 setFrameRate: 30];
560     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
561     [o_fullscreen_anim2 setDuration: 0.2];
562     [o_fullscreen_anim2 setFrameRate: 30];
563
564     [o_fullscreen_anim2 setDelegate: self];
565     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
566
567     [o_fullscreen_anim1 startAnimation];
568     /* fullscreenAnimation will be unlocked when animation ends */
569 }
570
571 - (void)hasBecomeFullscreen
572 {
573     [o_fullscreen_window makeFirstResponder: [[[VLCMain sharedInstance] controls] voutView]];
574
575     [o_fullscreen_window makeKeyWindow];
576     [o_fullscreen_window setAcceptsMouseMovedEvents: TRUE];
577
578     /* tell the fspanel to move itself to front next time it's triggered */
579     [[[[VLCMain sharedInstance] controls] fspanel] setVoutWasUpdated: (int)[[o_fullscreen_window screen] displayID]];
580
581     if([self isVisible])
582         [super orderOut: self];
583
584     [[[[VLCMain sharedInstance] controls] fspanel] setActive: nil];
585
586     b_fullscreen = YES;
587     [self unlockFullscreenAnimation];
588 }
589
590 - (void)leaveFullscreen
591 {
592     [self leaveFullscreenAndFadeOut: NO];
593 }
594
595 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
596 {
597     NSMutableDictionary *dict1, *dict2;
598     NSRect frame;
599
600     [self lockFullscreenAnimation];
601
602     b_fullscreen = NO;
603     [o_btn_fullscreen setState: NO];
604
605     /* We always try to do so */
606     [NSScreen unblackoutScreens];
607
608     /* Don't do anything if o_fullscreen_window is already closed */
609     if (!o_fullscreen_window)
610     {
611         [self unlockFullscreenAnimation];
612         return;
613     }
614
615     if (fadeout || MACOS_VERSION < 10.4f)
616     {
617         /* We don't animate if we are not visible or if we are running on
618         * Mac OS X <10.4 which doesn't support NSAnimation, instead we
619         * simply fade the display */
620         CGDisplayFadeReservationToken token;
621
622         CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
623         CGDisplayFade( token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES );
624
625         [[[[VLCMain sharedInstance] controls] fspanel] setNonActive: nil];
626         SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
627
628         /* Will release the lock */
629         [self hasEndedFullscreen];
630
631         /* Our window is hidden, and might be faded. We need to workaround that, so note it
632          * here */
633         b_window_is_invisible = YES;
634
635         CGDisplayFade( token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO );
636         CGReleaseDisplayFadeReservation( token);
637         return;
638     }
639
640     [self setAlphaValue: 0.0];
641     [self orderFront: self];
642
643     [[[[VLCMain sharedInstance] controls] fspanel] setNonActive: nil];
644     SetSystemUIMode( kUIModeNormal, kUIOptionAutoShowMenuBar);
645
646     if (o_fullscreen_anim1)
647     {
648         [o_fullscreen_anim1 stopAnimation];
649         [o_fullscreen_anim1 release];
650     }
651     if (o_fullscreen_anim2)
652     {
653         [o_fullscreen_anim2 stopAnimation];
654         [o_fullscreen_anim2 release];
655     }
656
657     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
658     frame.origin.x += [self frame].origin.x;
659     frame.origin.y += [self frame].origin.y;
660
661     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
662     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
663     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
664
665     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict2, nil]];
666     [dict2 release];
667
668     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
669     [o_fullscreen_anim2 setDuration: 0.3];
670     [o_fullscreen_anim2 setFrameRate: 30];
671
672     [o_fullscreen_anim2 setDelegate: self];
673
674     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
675
676     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
677     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
678     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
679
680     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict1, nil]];
681     [dict1 release];
682
683     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
684     [o_fullscreen_anim1 setDuration: 0.2];
685     [o_fullscreen_anim1 setFrameRate: 30];
686     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
687
688     /* Make sure o_fullscreen_window is the frontmost window */
689     [o_fullscreen_window orderFront: self];
690
691     [o_fullscreen_anim1 startAnimation];
692     /* fullscreenAnimation will be unlocked when animation ends */
693 }
694
695 - (void)hasEndedFullscreen
696 {
697     /* This function is private and should be only triggered at the end of the fullscreen change animation */
698     /* Make sure we don't see the o_view disappearing of the screen during this operation */
699     DisableScreenUpdates();
700     [o_view retain];
701     [o_view removeFromSuperviewWithoutNeedingDisplay];
702     [[self contentView] replaceSubview:o_temp_view with:o_view];
703     [o_view release];
704     [o_view setFrame:[o_temp_view frame]];
705     [self makeFirstResponder: o_view];
706     if ([self isVisible])
707         [super makeKeyAndOrderFront:self]; /* our version contains a workaround */
708     [o_fullscreen_window orderOut: self];
709     EnableScreenUpdates();
710
711     [o_fullscreen_window release];
712     o_fullscreen_window = nil;
713     [self setLevel:originalLevel];
714
715     [self unlockFullscreenAnimation];
716 }
717
718 - (void)animationDidEnd:(NSAnimation*)animation
719 {
720     NSArray *viewAnimations;
721
722     if ([animation currentValue] < 1.0)
723         return;
724
725     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
726     viewAnimations = [o_fullscreen_anim2 viewAnimations];
727     if ([viewAnimations count] >=1 &&
728         [[[viewAnimations objectAtIndex: 0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect])
729     {
730         /* Fullscreen ended */
731         [self hasEndedFullscreen];
732     }
733     else
734     {
735         /* Fullscreen started */
736         [self hasBecomeFullscreen];
737     }
738 }
739
740 - (void)orderOut: (id)sender
741 {
742     [super orderOut: sender];
743
744     /* Make sure we leave fullscreen */
745     [self leaveFullscreenAndFadeOut: YES];
746 }
747
748 - (void)makeKeyAndOrderFront: (id)sender
749 {
750     /* Hack
751      * when we exit fullscreen and fade out, we may endup in
752      * having a window that is faded. We can't have it fade in unless we
753      * animate again. */
754
755     if(!b_window_is_invisible)
756     {
757         /* Make sure we don't do it too much */
758         [super makeKeyAndOrderFront: sender];
759         return;
760     }
761
762     [super setAlphaValue:0.0f];
763     [super makeKeyAndOrderFront: sender];
764
765     NSMutableDictionary * dict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
766     [dict setObject:self forKey:NSViewAnimationTargetKey];
767     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
768
769     NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
770
771     [anim setAnimationBlockingMode: NSAnimationNonblocking];
772     [anim setDuration: 0.1];
773     [anim setFrameRate: 30];
774
775     [anim startAnimation];
776     b_window_is_invisible = NO;
777
778     /* fullscreenAnimation will be unlocked when animation ends */
779 }
780
781
782
783 /* Make sure setFrame gets executed on main thread especially if we are animating.
784  * (Thus we won't block the video output thread) */
785 - (void)setFrame:(NSRect)frame display:(BOOL)display animate:(BOOL)animate
786 {
787     struct { NSRect frame; BOOL display; BOOL animate;} args;
788     NSData *packedargs;
789
790     args.frame = frame;
791     args.display = display;
792     args.animate = animate;
793
794     packedargs = [NSData dataWithBytes:&args length:sizeof(args)];
795
796     [self performSelectorOnMainThread:@selector(setFrameOnMainThread:)
797                     withObject: packedargs waitUntilDone: YES];
798 }
799
800 - (void)setFrameOnMainThread:(NSData*)packedargs
801 {
802     struct args { NSRect frame; BOOL display; BOOL animate; } * args = (struct args*)[packedargs bytes];
803
804     if( args->animate )
805     {
806         /* Make sure we don't block too long and set up a non blocking animation */
807         NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
808             self, NSViewAnimationTargetKey,
809             [NSValue valueWithRect:[self frame]], NSViewAnimationStartFrameKey,
810             [NSValue valueWithRect:args->frame], NSViewAnimationEndFrameKey, nil];
811
812         NSViewAnimation * anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:dict, nil]];
813
814         [anim setAnimationBlockingMode: NSAnimationNonblocking];
815         [anim setDuration: 0.4];
816         [anim setFrameRate: 30];
817         [anim startAnimation];
818     }
819     else {
820         [super setFrame:args->frame display:args->display animate:args->animate];
821     }
822
823 }
824 @end
825
826 /*****************************************************************************
827  * embeddedbackground
828  *****************************************************************************/
829
830
831 @implementation embeddedbackground
832
833 - (void)dealloc
834 {
835     [self unregisterDraggedTypes];
836     [super dealloc];
837 }
838
839 - (void)awakeFromNib
840 {
841     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSTIFFPboardType,
842                                    NSFilenamesPboardType, nil]];
843     [self addSubview: o_timeslider];
844     [self addSubview: o_scrollfield];
845     [self addSubview: o_time];
846     [self addSubview: o_main_pgbar];
847     [self addSubview: o_btn_backward];
848     [self addSubview: o_btn_forward];
849     [self addSubview: o_btn_fullscreen];
850     [self addSubview: o_btn_equalizer];
851     [self addSubview: o_btn_playlist];
852     [self addSubview: o_btn_play];
853     [self addSubview: o_btn_prev];
854     [self addSubview: o_btn_stop];
855     [self addSubview: o_btn_next];
856     [self addSubview: o_btn_volume_down];
857     [self addSubview: o_volumeslider];
858     [self addSubview: o_btn_volume_up];
859     [self addSubview: o_searchfield];
860 }
861
862 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
863 {
864     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask])
865         == NSDragOperationGeneric)
866     {
867         return NSDragOperationGeneric;
868     }
869     else
870     {
871         return NSDragOperationNone;
872     }
873 }
874
875 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
876 {
877     return YES;
878 }
879
880 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
881 {
882     NSPasteboard *o_paste = [sender draggingPasteboard];
883     NSArray *o_types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
884     NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
885     NSData *o_carried_data = [o_paste dataForType:o_desired_type];
886     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
887     
888     if( o_carried_data )
889     {
890         if ([o_desired_type isEqualToString:NSFilenamesPboardType])
891         {
892             int i;
893             NSArray *o_array = [NSArray array];
894             NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType]
895                                  sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
896             
897             for( i = 0; i < (int)[o_values count]; i++)
898             {
899                 NSDictionary *o_dic;
900                 o_dic = [NSDictionary dictionaryWithObject:[o_values objectAtIndex:i] forKey:@"ITEM_URL"];
901                 o_array = [o_array arrayByAddingObject: o_dic];
902             }
903             if( b_autoplay )
904                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
905             else
906                 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
907             return YES;
908         }
909     }
910     [self setNeedsDisplay:YES];
911     return YES;
912 }
913
914 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
915 {
916     [self setNeedsDisplay:YES];
917 }
918
919 - (void)drawRect:(NSRect)rect
920 {
921     NSImage *leftImage = [NSImage imageNamed:@"display_left"];
922     NSImage *middleImage = [NSImage imageNamed:@"display_middle"];
923     NSImage *rightImage = [NSImage imageNamed:@"display_right"];
924     [middleImage setSize:NSMakeSize(NSWidth( [self bounds] ) - 134 - [leftImage size].width - [rightImage size].width, [middleImage size].height)];
925     [middleImage setScalesWhenResized:YES];
926     [leftImage compositeToPoint:NSMakePoint( 122., 40. ) operation:NSCompositeSourceOver];
927     [middleImage compositeToPoint:NSMakePoint( 122. + [leftImage size].width, 40. ) operation:NSCompositeSourceOver];
928     [rightImage compositeToPoint:NSMakePoint( NSWidth( [self bounds] ) - 12 - [rightImage size].width, 40. ) operation:NSCompositeSourceOver];
929 }
930
931 - (void)mouseDown:(NSEvent *)event
932 {
933     dragStart = [self convertPoint:[event locationInWindow] fromView:nil];
934 }
935
936 - (void)mouseDragged:(NSEvent *)event
937 {
938     NSPoint dragLocation = [self convertPoint:[event locationInWindow] fromView:nil];
939     NSPoint winOrigin = [o_window frame].origin;
940
941     NSPoint newOrigin = NSMakePoint(winOrigin.x + (dragLocation.x - dragStart.x),
942                                     winOrigin.y + (dragLocation.y - dragStart.y));
943     [o_window setFrameOrigin: newOrigin];
944 }
945
946 @end
947
948 /*****************************************************************************
949  * statusbar
950  *****************************************************************************/
951
952
953 @implementation statusbar
954 - (void)awakeFromNib
955 {
956     [self addSubview: o_text];
957         mainwindow = YES;
958 }
959
960 - (void)resignMainWindow
961 {
962         mainwindow = NO;
963         [self needsDisplay];
964 }
965
966 - (void)becomeMainWindow
967 {
968         mainwindow = YES;
969         [self needsDisplay];
970 }
971
972 - (void)drawRect:(NSRect)rect
973 {
974         if(mainwindow)
975                 [[NSColor colorWithCalibratedRed:0.820
976                                                                    green:0.843
977                                                                         blue:0.886
978                                                                    alpha:1.0] set];
979         else
980                 [[NSColor colorWithCalibratedWhite:0.91 alpha:1.0] set];
981         NSRectFill(rect);
982         /*NSRect divider = rect;
983         divider.origin.y += divider.size.height - 1;
984         divider.size.height = 1;
985         [[NSColor colorWithCalibratedWhite:0.65 alpha:1.] set];
986         NSRectFill(divider);*/
987 }
988 @end