]> git.sesse.net Git - vlc/blob - modules/gui/macosx/Windows.m
macosx: add NSFullScreenWindowMask to compatibilityFixes.h and avoid NSAnimationConte...
[vlc] / modules / gui / macosx / Windows.m
1 /*****************************************************************************
2  * Windows.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2012-2014 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
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 #import "Windows.h"
26 #import "intf.h"
27 #import "CoreInteraction.h"
28 #import "ControlsBar.h"
29 #import "VideoView.h"
30 #import "CompatibilityFixes.h"
31
32 /*****************************************************************************
33  * VLCWindow
34  *
35  *  Missing extension to NSWindow
36  *****************************************************************************/
37
38 @implementation VLCWindow
39
40 @synthesize hasActiveVideo=b_has_active_video;
41 @synthesize fullscreen=b_fullscreen;
42
43 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
44                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
45 {
46     self = [super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag];
47     if (self) {
48         /* we don't want this window to be restored on relaunch */
49         if (!OSX_SNOW_LEOPARD)
50             [self setRestorable:NO];
51     }
52     return self;
53 }
54
55 - (void)setCanBecomeKeyWindow: (BOOL)canBecomeKey
56 {
57     b_isset_canBecomeKeyWindow = YES;
58     b_canBecomeKeyWindow = canBecomeKey;
59 }
60
61 - (BOOL)canBecomeKeyWindow
62 {
63     if (b_isset_canBecomeKeyWindow)
64         return b_canBecomeKeyWindow;
65
66     return [super canBecomeKeyWindow];
67 }
68
69 - (void)setCanBecomeMainWindow: (BOOL)canBecomeMain
70 {
71     b_isset_canBecomeMainWindow = YES;
72     b_canBecomeMainWindow = canBecomeMain;
73 }
74
75 - (BOOL)canBecomeMainWindow
76 {
77     if (b_isset_canBecomeMainWindow)
78         return b_canBecomeMainWindow;
79
80     return [super canBecomeMainWindow];
81 }
82
83 - (void)closeAndAnimate: (BOOL)animate
84 {
85     NSInvocation *invoc;
86
87     if (!animate) {
88         [super close];
89         return;
90     }
91
92     // TODO this callback stuff does not work and is not needed
93     invoc = [[[NSInvocation alloc] init] autorelease];
94     [invoc setSelector:@selector(close)];
95     [invoc setTarget: self];
96
97     if (![self isVisible] || [self alphaValue] == 0.0) {
98         [super close];
99         return;
100     }
101
102     [self orderOut: self animate: YES callback: invoc];
103 }
104
105 - (void)orderOut: (id)sender animate: (BOOL)animate
106 {
107     NSInvocation *invoc = [[[NSInvocation alloc] init] autorelease];
108     [invoc setSelector:@selector(orderOut:)];
109     [invoc setTarget: self];
110     [invoc setArgument: sender atIndex: 2];
111     [self orderOut: sender animate: animate callback: invoc];
112 }
113
114 - (void)orderOut: (id)sender animate: (BOOL)animate callback:(NSInvocation *)callback
115 {
116     NSViewAnimation *anim;
117     NSViewAnimation *current_anim;
118     NSMutableDictionary *dict;
119
120     if (!animate) {
121         [self orderOut: sender];
122         return;
123     }
124
125     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
126
127     [dict setObject:self forKey:NSViewAnimationTargetKey];
128
129     [dict setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
130     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
131     [dict release];
132
133     [anim setAnimationBlockingMode:NSAnimationNonblocking];
134     [anim setDuration:0.9];
135     [anim setFrameRate:30];
136     [anim setUserInfo:callback];
137     [anim setDelegate:self];
138
139     @synchronized(self) {
140         current_anim = self->o_current_animation;
141
142         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeOutEffect && [current_anim isAnimating]) {
143             [anim release];
144         } else {
145             if (current_anim) {
146                 [current_anim stopAnimation];
147                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
148                 [current_anim release];
149             }
150             else
151                 [anim setCurrentProgress:1.0 - [self alphaValue]];
152             self->o_current_animation = anim;
153             [anim startAnimation];
154         }
155     }
156 }
157
158 - (void)orderFront: (id)sender animate: (BOOL)animate
159 {
160     NSViewAnimation *anim;
161     NSViewAnimation *current_anim;
162     NSMutableDictionary *dict;
163
164     if (!animate) {
165         [super orderFront: sender];
166         [self setAlphaValue: 1.0];
167         return;
168     }
169
170     if (![self isVisible]) {
171         [self setAlphaValue: 0.0];
172         [super orderFront: sender];
173     }
174     else if ([self alphaValue] == 1.0) {
175         [super orderFront: self];
176         return;
177     }
178
179     dict = [[NSMutableDictionary alloc] initWithCapacity:2];
180
181     [dict setObject:self forKey:NSViewAnimationTargetKey];
182
183     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
184     anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
185     [dict release];
186
187     [anim setAnimationBlockingMode:NSAnimationNonblocking];
188     [anim setDuration:0.5];
189     [anim setFrameRate:30];
190     [anim setDelegate:self];
191
192     @synchronized(self) {
193         current_anim = self->o_current_animation;
194
195         if ([[[current_anim viewAnimations] objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] == NSViewAnimationFadeInEffect && [current_anim isAnimating]) {
196             [anim release];
197         } else {
198             if (current_anim) {
199                 [current_anim stopAnimation];
200                 [anim setCurrentProgress:1.0 - [current_anim currentProgress]];
201                 [current_anim release];
202             }
203             else
204                 [anim setCurrentProgress:[self alphaValue]];
205             self->o_current_animation = anim;
206             [self orderFront: sender];
207             [anim startAnimation];
208         }
209     }
210 }
211
212 - (void)animationDidEnd:(NSAnimation*)anim
213 {
214     if ([self alphaValue] <= 0.0) {
215         NSInvocation * invoc;
216         [super orderOut: nil];
217         [self setAlphaValue: 1.0];
218         if ((invoc = [anim userInfo])) {
219             [invoc invoke];
220         }
221     }
222 }
223
224 - (VLCVoutView *)videoView
225 {
226     if ([[self contentView] class] == [VLCVoutView class])
227         return (VLCVoutView *)[self contentView];
228
229     return nil;
230 }
231
232 - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
233 {
234     if (!screen)
235         screen = [self screen];
236     NSRect screenRect = [screen frame];
237     NSRect constrainedRect = [super constrainFrameRect:frameRect toScreen:screen];
238
239     /*
240      * Ugly workaround!
241      * With Mavericks, there is a nasty bug resulting in grey bars on top in fullscreen mode.
242      * It looks like this is enforced by the os because the window is in the way for the menu bar.
243      *
244      * According to the documentation, this constraining can be changed by overwriting this
245      * method. But in this situation, even the received frameRect is already contrained with the
246      * menu bars height substracted. This case is detected here, and the full height is
247      * enforced again.
248      *
249      * See #9469 and radar://15583566
250      */
251
252     BOOL b_inFullscreen = [self fullscreen] || ([self respondsToSelector:@selector(inFullscreenTransition)] && [(VLCVideoWindowCommon *)self inFullscreenTransition]);
253
254     if(OSX_MAVERICKS && b_inFullscreen && constrainedRect.size.width == screenRect.size.width
255           && constrainedRect.size.height != screenRect.size.height
256           && abs(screenRect.size.height - constrainedRect.size.height) <= 25.) {
257
258         msg_Dbg(VLCIntf, "Contrain window height %.1f to screen height %.1f",
259                 constrainedRect.size.height, screenRect.size.height);
260         constrainedRect.size.height = screenRect.size.height;
261     }
262
263     return constrainedRect;
264 }
265
266 @end
267
268
269 /*****************************************************************************
270  * VLCVideoWindowCommon
271  *
272  *  Common code for main window, detached window and extra video window
273  *****************************************************************************/
274
275 @interface VLCVideoWindowCommon (Internal)
276 - (void)customZoom:(id)sender;
277 - (void)hasBecomeFullscreen;
278 - (void)leaveFullscreenAndFadeOut:(BOOL)fadeout;
279 - (void)hasEndedFullscreen;
280 @end
281
282 @implementation VLCVideoWindowCommon
283
284 @synthesize videoView=o_video_view;
285 @synthesize controlsBar=o_controls_bar;
286 @synthesize inFullscreenTransition=b_in_fullscreen_transition;
287
288 #pragma mark -
289 #pragma mark Init
290
291 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
292                   backing:(NSBackingStoreType)backingType defer:(BOOL)flag
293 {
294     b_dark_interface = config_GetInt(VLCIntf, "macosx-interfacestyle");
295
296     if (b_dark_interface) {
297         styleMask = NSBorderlessWindowMask;
298 #ifdef MAC_OS_X_VERSION_10_7
299         if (!OSX_SNOW_LEOPARD)
300             styleMask |= NSResizableWindowMask;
301 #endif
302     }
303
304     self = [super initWithContentRect:contentRect styleMask:styleMask
305                               backing:backingType defer:flag];
306
307     /* we want to be moveable regardless of our style */
308     [self setMovableByWindowBackground: YES];
309     [self setCanBecomeKeyWindow:YES];
310
311     o_temp_view = [[NSView alloc] init];
312     [o_temp_view setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable];
313
314     return self;
315 }
316
317 - (void)dealloc
318 {
319     [o_temp_view release];
320     [super dealloc];
321 }
322
323 - (void)awakeFromNib
324 {
325     BOOL b_nativeFullscreenMode = NO;
326 #ifdef MAC_OS_X_VERSION_10_7
327     if (!OSX_SNOW_LEOPARD)
328         b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
329 #endif
330
331     if (b_nativeFullscreenMode) {
332         [self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
333     } else {
334         [o_titlebar_view setFullscreenButtonHidden: YES];
335     }
336
337     [super awakeFromNib];
338 }
339
340 - (void)setTitle:(NSString *)title
341 {
342     if (!title || [title length] < 1)
343         return;
344
345     if (b_dark_interface && o_titlebar_view)
346         [o_titlebar_view setWindowTitle: title];
347
348     [super setTitle: title];
349 }
350
351 #pragma mark -
352 #pragma mark zoom / minimize / close
353
354 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
355 {
356     SEL s_menuAction = [menuItem action];
357
358     if ((s_menuAction == @selector(performClose:)) || (s_menuAction == @selector(performMiniaturize:)) || (s_menuAction == @selector(performZoom:)))
359         return YES;
360
361     return [super validateMenuItem:menuItem];
362 }
363
364 - (BOOL)windowShouldClose:(id)sender
365 {
366     return YES;
367 }
368
369 - (void)performClose:(id)sender
370 {
371     if (!([self styleMask] & NSTitledWindowMask)) {
372         [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowWillCloseNotification object:self];
373
374         [self close];
375     } else
376         [super performClose: sender];
377 }
378
379 - (void)performMiniaturize:(id)sender
380 {
381     if (!([self styleMask] & NSTitledWindowMask))
382         [self miniaturize: sender];
383     else
384         [super performMiniaturize: sender];
385 }
386
387 - (void)performZoom:(id)sender
388 {
389     if (!([self styleMask] & NSTitledWindowMask))
390         [self customZoom: sender];
391     else
392         [super performZoom: sender];
393 }
394
395 - (void)zoom:(id)sender
396 {
397     if (!([self styleMask] & NSTitledWindowMask))
398         [self customZoom: sender];
399     else
400         [super zoom: sender];
401 }
402
403 /**
404  * Given a proposed frame rectangle, return a modified version
405  * which will fit inside the screen.
406  *
407  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
408  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
409  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
410  *    Copyright (C) 1996 Free Software Foundation, Inc.
411  */
412 - (NSRect) customConstrainFrameRect: (NSRect)frameRect toScreen: (NSScreen*)screen
413 {
414     NSRect screenRect = [screen visibleFrame];
415     float difference;
416
417     /* Move top edge of the window inside the screen */
418     difference = NSMaxY (frameRect) - NSMaxY (screenRect);
419     if (difference > 0) {
420         frameRect.origin.y -= difference;
421     }
422
423     /* If the window is resizable, resize it (if needed) so that the
424      bottom edge is on the screen or can be on the screen when the user moves
425      the window */
426     difference = NSMaxY (screenRect) - NSMaxY (frameRect);
427     if (_styleMask & NSResizableWindowMask) {
428         float difference2;
429
430         difference2 = screenRect.origin.y - frameRect.origin.y;
431         difference2 -= difference;
432         // Take in account the space between the top of window and the top of the
433         // screen which can be used to move the bottom of the window on the screen
434         if (difference2 > 0) {
435             frameRect.size.height -= difference2;
436             frameRect.origin.y += difference2;
437         }
438
439         /* Ensure that resizing doesn't makewindow smaller than minimum */
440         difference2 = [self minSize].height - frameRect.size.height;
441         if (difference2 > 0) {
442             frameRect.size.height += difference2;
443             frameRect.origin.y -= difference2;
444         }
445     }
446
447     return frameRect;
448 }
449
450 #define DIST 3
451
452 /**
453  Zooms the receiver.   This method calls the delegate method
454  windowShouldZoom:toFrame: to determine if the window should
455  be allowed to zoom to full screen.
456  *
457  * This method is based upon NSWindow.m, part of the GNUstep GUI Library, licensed under LGPLv2+.
458  *    Authors:  Scott Christley <scottc@net-community.com>, Venkat Ajjanagadde <venkat@ocbi.com>,
459  *              Felipe A. Rodriguez <far@ix.netcom.com>, Richard Frith-Macdonald <richard@brainstorm.co.uk>
460  *    Copyright (C) 1996 Free Software Foundation, Inc.
461  */
462 - (void) customZoom: (id)sender
463 {
464     NSRect maxRect = [[self screen] visibleFrame];
465     NSRect currentFrame = [self frame];
466
467     if ([[self delegate] respondsToSelector: @selector(windowWillUseStandardFrame:defaultFrame:)]) {
468         maxRect = [[self delegate] windowWillUseStandardFrame: self defaultFrame: maxRect];
469     }
470
471     maxRect = [self customConstrainFrameRect: maxRect toScreen: [self screen]];
472
473     // Compare the new frame with the current one
474     if ((abs(NSMaxX(maxRect) - NSMaxX(currentFrame)) < DIST)
475         && (abs(NSMaxY(maxRect) - NSMaxY(currentFrame)) < DIST)
476         && (abs(NSMinX(maxRect) - NSMinX(currentFrame)) < DIST)
477         && (abs(NSMinY(maxRect) - NSMinY(currentFrame)) < DIST)) {
478         // Already in zoomed mode, reset user frame, if stored
479         if ([self frameAutosaveName] != nil) {
480             [self setFrame: previousSavedFrame display: YES animate: YES];
481             [self saveFrameUsingName: [self frameAutosaveName]];
482         }
483         return;
484     }
485
486     if ([self frameAutosaveName] != nil) {
487         [self saveFrameUsingName: [self frameAutosaveName]];
488         previousSavedFrame = [self frame];
489     }
490
491     [self setFrame: maxRect display: YES animate: YES];
492 }
493
494 #pragma mark -
495 #pragma mark Video window resizing logic
496
497 - (void)setWindowLevel:(NSInteger)i_state
498 {
499     if (var_InheritBool(VLCIntf, "video-wallpaper") || [self level] < NSNormalWindowLevel)
500         return;
501
502     if (!b_fullscreen && !b_in_fullscreen_transition)
503         [self setLevel: i_state];
504
505     // save it for restore if window is currently minimized or in fullscreen
506     i_originalLevel = i_state;
507 }
508
509 - (NSRect)getWindowRectForProposedVideoViewSize:(NSSize)size
510 {
511     NSSize windowMinSize = [self minSize];
512     NSRect screenFrame = [[self screen] visibleFrame];
513
514     NSPoint topleftbase = NSMakePoint(0, [self frame].size.height);
515     NSPoint topleftscreen = [self convertBaseToScreen: topleftbase];
516
517     unsigned int i_width = size.width;
518     unsigned int i_height = size.height;
519     if (i_width < windowMinSize.width)
520         i_width = windowMinSize.width;
521     if (i_height < f_min_video_height)
522         i_height = f_min_video_height;
523
524     /* Calculate the window's new size */
525     NSRect new_frame;
526     new_frame.size.width = [self frame].size.width - [o_video_view frame].size.width + i_width;
527     new_frame.size.height = [self frame].size.height - [o_video_view frame].size.height + i_height;
528     new_frame.origin.x = topleftscreen.x;
529     new_frame.origin.y = topleftscreen.y - new_frame.size.height;
530
531     /* make sure the window doesn't exceed the screen size the window is on */
532     if (new_frame.size.width > screenFrame.size.width) {
533         new_frame.size.width = screenFrame.size.width;
534         new_frame.origin.x = screenFrame.origin.x;
535     }
536     if (new_frame.size.height > screenFrame.size.height) {
537         new_frame.size.height = screenFrame.size.height;
538         new_frame.origin.y = screenFrame.origin.y;
539     }
540     if (new_frame.origin.y < screenFrame.origin.y)
541         new_frame.origin.y = screenFrame.origin.y;
542
543     CGFloat right_screen_point = screenFrame.origin.x + screenFrame.size.width;
544     CGFloat right_window_point = new_frame.origin.x + new_frame.size.width;
545     if (right_window_point > right_screen_point)
546         new_frame.origin.x -= (right_window_point - right_screen_point);
547
548     return new_frame;
549 }
550
551 - (void)resizeWindow
552 {
553     // VOUT_WINDOW_SET_SIZE is triggered when exiting fullscreen. This event is ignored here
554     // to avoid interference with the animation.
555     if ([self fullscreen] || b_in_fullscreen_transition)
556         return;
557
558     NSRect window_rect = [self getWindowRectForProposedVideoViewSize:nativeVideoSize];
559     [[self animator] setFrame:window_rect display:YES];
560 }
561
562 - (void)setNativeVideoSize:(NSSize)size
563 {
564     nativeVideoSize = size;
565
566     if (var_InheritBool(VLCIntf, "macosx-video-autoresize") && !var_InheritBool(VLCIntf, "video-wallpaper"))
567         [self resizeWindow];
568 }
569
570 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
571 {
572     if (![[VLCMain sharedInstance] activeVideoPlayback] || nativeVideoSize.width == 0. || nativeVideoSize.height == 0. || window != self)
573         return proposedFrameSize;
574
575     // needed when entering lion fullscreen mode
576     if (b_in_fullscreen_transition || [self fullscreen])
577         return proposedFrameSize;
578
579     if ([o_video_view isHidden])
580         return proposedFrameSize;
581
582     if ([[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]) {
583         NSRect videoWindowFrame = [self frame];
584         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
585         NSRect contentRect = [self contentRectForFrameRect:videoWindowFrame];
586         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
587         float marginx = contentRect.size.width - viewRect.size.width;
588         if (o_titlebar_view && b_dark_interface)
589             marginy += [o_titlebar_view frame].size.height;
590
591         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
592     }
593
594     return proposedFrameSize;
595 }
596
597 - (void)windowWillMiniaturize:(NSNotification *)notification
598 {
599     // Set level to normal as a workaround for Mavericks bug causing window
600     // to vanish from screen, see radar://15473716
601     i_originalLevel = [self level];
602     [self setLevel: NSNormalWindowLevel];
603 }
604
605 - (void)windowDidDeminiaturize:(NSNotification *)notification
606 {
607     [self setLevel: i_originalLevel];
608 }
609
610 #pragma mark -
611 #pragma mark Mouse cursor handling
612
613 //  NSTimer selectors require this function signature as per Apple's docs
614 - (void)hideMouseCursor:(NSTimer *)timer
615 {
616     [NSCursor setHiddenUntilMouseMoves: YES];
617 }
618
619 - (void)recreateHideMouseTimer
620 {
621     if (t_hide_mouse_timer != nil) {
622         [t_hide_mouse_timer invalidate];
623         [t_hide_mouse_timer release];
624     }
625
626     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
627                                                           target:self
628                                                         selector:@selector(hideMouseCursor:)
629                                                         userInfo:nil
630                                                          repeats:NO];
631     [t_hide_mouse_timer retain];
632 }
633
634 //  Called automatically if window's acceptsMouseMovedEvents property is true
635 - (void)mouseMoved:(NSEvent *)theEvent
636 {
637     if (b_fullscreen)
638         [self recreateHideMouseTimer];
639
640     [super mouseMoved: theEvent];
641 }
642
643 #pragma mark -
644 #pragma mark Lion native fullscreen handling
645
646 - (void)becomeKeyWindow
647 {
648     [super becomeKeyWindow];
649
650     // change fspanel state for the case when multiple windows are in fullscreen
651     if ([self hasActiveVideo] && [self fullscreen])
652         [[[VLCMainWindow sharedInstance] fsPanel] setActive:nil];
653     else
654         [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
655 }
656
657 - (void)resignKeyWindow
658 {
659     [super resignKeyWindow];
660
661     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
662 }
663
664 -(NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow *)window
665 {
666     if (window == self) {
667         return [NSArray arrayWithObject:window];
668     }
669
670     return nil;
671 }
672
673 - (NSArray*)customWindowsToExitFullScreenForWindow:(NSWindow*)window
674 {
675     if (window == self) {
676         return [NSArray arrayWithObject:window];
677     }
678
679     return nil;
680 }
681
682 - (void)window:window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration
683 {
684     [window setStyleMask:([window styleMask] | NSFullScreenWindowMask)];
685
686     NSScreen *screen = [window screen];
687     NSRect screenFrame = [screen frame];
688
689     [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
690         [context setDuration:0.5 * duration];
691         [[window animator] setFrame:screenFrame display:YES];
692     } completionHandler:nil];
693 }
694
695 - (void)window:window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration
696 {
697     [window setStyleMask:([window styleMask] & ~NSFullScreenWindowMask)];
698     [[window animator] setFrame:frameBeforeLionFullscreen display:YES animate:YES];
699
700     [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
701         [context setDuration:0.5 * duration];
702         [[window animator] setFrame:frameBeforeLionFullscreen display:YES animate:YES];
703     } completionHandler:nil];
704 }
705
706 - (void)windowWillEnterFullScreen:(NSNotification *)notification
707 {
708     // workaround, see #6668
709     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
710
711     i_originalLevel = [self level];
712     // b_fullscreen and b_in_fullscreen_transition must not be true yet
713     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
714     [self setLevel:NSNormalWindowLevel];
715
716     b_in_fullscreen_transition = YES;
717
718     var_SetBool(pl_Get(VLCIntf), "fullscreen", true);
719
720     frameBeforeLionFullscreen = [self frame];
721
722     if ([self hasActiveVideo]) {
723         vout_thread_t *p_vout = getVoutForActiveWindow();
724         if (p_vout) {
725             var_SetBool(p_vout, "fullscreen", true);
726             vlc_object_release(p_vout);
727         }
728     }
729
730     if ([self hasActiveVideo])
731         [[VLCMainWindow sharedInstance] recreateHideMouseTimer];
732
733     if (b_dark_interface) {
734         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
735
736         NSRect winrect;
737         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
738         winrect = [self frame];
739
740         winrect.size.height = winrect.size.height - f_titleBarHeight;
741         [self setFrame: winrect display:NO animate:NO];
742     }
743
744     [o_video_view setFrame: [[self contentView] frame]];
745     if (![o_video_view isHidden]) {
746         [[o_controls_bar bottomBarView] setHidden: YES];
747     }
748
749     [self setMovableByWindowBackground: NO];
750 }
751
752 - (void)windowDidEnterFullScreen:(NSNotification *)notification
753 {
754     // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
755     // But this creates some problems when leaving fs over remote intfs, so activate app here.
756     [NSApp activateIgnoringOtherApps:YES];
757
758     [self setFullscreen: YES];
759     b_in_fullscreen_transition = NO;
760
761     if ([self hasActiveVideo]) {
762         [[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: self];
763         if (![o_video_view isHidden])
764             [[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
765     }
766
767     NSArray *subviews = [[self videoView] subviews];
768     NSUInteger count = [subviews count];
769
770     for (NSUInteger x = 0; x < count; x++) {
771         if ([[subviews objectAtIndex:x] respondsToSelector:@selector(reshape)])
772             [[subviews objectAtIndex:x] reshape];
773     }
774
775 }
776
777 - (void)windowWillExitFullScreen:(NSNotification *)notification
778 {
779     b_in_fullscreen_transition = YES;
780     [self setFullscreen: NO];
781
782     var_SetBool(pl_Get(VLCIntf), "fullscreen", false);
783
784     if ([self hasActiveVideo]) {
785         vout_thread_t *p_vout = getVoutForActiveWindow();
786         if (p_vout) {
787             var_SetBool(p_vout, "fullscreen", false);
788             vlc_object_release(p_vout);
789         }
790     }
791
792     [NSCursor setHiddenUntilMouseMoves: NO];
793     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
794
795
796     if (b_dark_interface) {
797         NSRect winrect;
798         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
799
800         winrect = [o_video_view frame];
801         winrect.size.height -= f_titleBarHeight;
802         [o_video_view setFrame: winrect];
803
804         winrect = [self frame];
805         [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
806                                               winrect.size.width, f_titleBarHeight)];
807         [[self contentView] addSubview: o_titlebar_view];
808
809         winrect.size.height = winrect.size.height + f_titleBarHeight;
810         [self setFrame: winrect display:NO animate:NO];
811     }
812
813     NSRect videoViewFrame = [o_video_view frame];
814     videoViewFrame.origin.y += [o_controls_bar height];
815     videoViewFrame.size.height -= [o_controls_bar height];
816     [o_video_view setFrame: videoViewFrame];
817
818     if (![o_video_view isHidden]) {
819         [[o_controls_bar bottomBarView] setHidden: NO];
820     }
821
822     [self setMovableByWindowBackground: YES];
823 }
824
825 - (void)windowDidExitFullScreen:(NSNotification *)notification
826 {
827     b_in_fullscreen_transition = NO;
828
829     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
830     [self setLevel:i_originalLevel];
831 }
832
833 #pragma mark -
834 #pragma mark Fullscreen Logic
835
836 - (void)enterFullscreen
837 {
838     NSMutableDictionary *dict1, *dict2;
839     NSScreen *screen;
840     NSRect screen_rect;
841     NSRect rect;
842     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
843
844     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
845
846     if (!screen) {
847         msg_Dbg(VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode");
848         screen = [self screen];
849     }
850     if (!screen) {
851         msg_Dbg(VLCIntf, "Using deepest screen");
852         screen = [NSScreen deepestScreen];
853     }
854
855     screen_rect = [screen frame];
856
857     if (o_controls_bar)
858         [o_controls_bar setFullscreenState:YES];
859     [[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:YES];
860
861     [[VLCMainWindow sharedInstance] recreateHideMouseTimer];
862
863     if (blackout_other_displays)
864         [screen blackoutOtherScreens];
865
866     /* Make sure we don't see the window flashes in float-on-top mode */
867     i_originalLevel = [self level];
868     // b_fullscreen must not be true yet
869     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
870     [self setLevel:NSNormalWindowLevel];
871
872     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
873     if (!o_fullscreen_window) {
874         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
875
876         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
877         rect.origin.x += [self frame].origin.x;
878         rect.origin.y += [self frame].origin.y;
879         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
880         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
881         [o_fullscreen_window setCanBecomeKeyWindow: YES];
882         [o_fullscreen_window setCanBecomeMainWindow: YES];
883         [o_fullscreen_window setHasActiveVideo: YES];
884         [o_fullscreen_window setFullscreen: YES];
885
886         if (![self isVisible] || [self alphaValue] == 0.0) {
887             /* We don't animate if we are not visible, instead we
888              * simply fade the display */
889             CGDisplayFadeReservationToken token;
890
891             if (blackout_other_displays) {
892                 CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
893                 CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
894             }
895
896             [screen setFullscreenPresentationOptions];
897
898             [o_video_view retain];
899             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
900             [o_temp_view setFrame:[o_video_view frame]];
901             [o_fullscreen_window setContentView:o_video_view];
902             [o_video_view release];
903
904             [o_fullscreen_window makeKeyAndOrderFront:self];
905             [o_fullscreen_window orderFront:self animate:YES];
906
907             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
908             [o_fullscreen_window setLevel:NSNormalWindowLevel];
909
910             if (blackout_other_displays) {
911                 CGDisplayFade(token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
912                 CGReleaseDisplayFadeReservation(token);
913             }
914
915             /* Will release the lock */
916             [self hasBecomeFullscreen];
917
918             return;
919         }
920
921         /* Make sure video view gets visible in case the playlist was visible before */
922         b_video_view_was_hidden = [o_video_view isHidden];
923         [o_video_view setHidden: NO];
924
925         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
926         NSDisableScreenUpdates();
927         [o_video_view retain];
928         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
929         [o_temp_view setFrame:[o_video_view frame]];
930         [o_fullscreen_window setContentView:o_video_view];
931         [o_video_view release];
932         [o_fullscreen_window makeKeyAndOrderFront:self];
933         NSEnableScreenUpdates();
934     }
935
936     /* We are in fullscreen (and no animation is running) */
937     if ([self fullscreen]) {
938         /* Make sure we are hidden */
939         [self orderOut: self];
940
941         return;
942     }
943
944     if (o_fullscreen_anim1) {
945         [o_fullscreen_anim1 stopAnimation];
946         [o_fullscreen_anim1 release];
947     }
948     if (o_fullscreen_anim2) {
949         [o_fullscreen_anim2 stopAnimation];
950         [o_fullscreen_anim2 release];
951     }
952
953     [screen setFullscreenPresentationOptions];
954
955     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
956     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
957
958     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
959     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
960
961     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
962     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
963     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
964
965     /* Strategy with NSAnimation allocation:
966      - Keep at most 2 animation at a time
967      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
968      */
969     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
970     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
971
972     [dict1 release];
973     [dict2 release];
974
975     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
976     [o_fullscreen_anim1 setDuration: 0.3];
977     [o_fullscreen_anim1 setFrameRate: 30];
978     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
979     [o_fullscreen_anim2 setDuration: 0.2];
980     [o_fullscreen_anim2 setFrameRate: 30];
981
982     [o_fullscreen_anim2 setDelegate: self];
983     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
984
985     [o_fullscreen_anim1 startAnimation];
986     /* fullscreenAnimation will be unlocked when animation ends */
987
988     b_in_fullscreen_transition = YES;
989 }
990
991 - (void)hasBecomeFullscreen
992 {
993     if ([[o_video_view subviews] count] > 0)
994         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
995
996     [o_fullscreen_window makeKeyWindow];
997     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
998
999     /* tell the fspanel to move itself to front next time it's triggered */
1000     [[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: o_fullscreen_window];
1001     [[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
1002
1003     if ([self isVisible])
1004         [self orderOut: self];
1005
1006     b_in_fullscreen_transition = NO;
1007     [self setFullscreen:YES];
1008 }
1009
1010 - (void)leaveFullscreen
1011 {
1012     [self leaveFullscreenAndFadeOut: NO];
1013 }
1014
1015 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1016 {
1017     NSMutableDictionary *dict1, *dict2;
1018     NSRect frame;
1019     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
1020
1021     if (o_controls_bar)
1022         [o_controls_bar setFullscreenState:NO];
1023     [[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:NO];
1024
1025     /* We always try to do so */
1026     [NSScreen unblackoutScreens];
1027
1028     [[o_video_view window] makeKeyAndOrderFront: nil];
1029
1030     /* Don't do anything if o_fullscreen_window is already closed */
1031     if (!o_fullscreen_window) {
1032         return;
1033     }
1034
1035     if (fadeout) {
1036         /* We don't animate if we are not visible, instead we
1037          * simply fade the display */
1038         CGDisplayFadeReservationToken token;
1039
1040         if (blackout_other_displays) {
1041             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
1042             CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
1043         }
1044
1045         [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
1046         [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
1047
1048         /* Will release the lock */
1049         [self hasEndedFullscreen];
1050
1051         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1052          * here */
1053         b_window_is_invisible = YES;
1054
1055         if (blackout_other_displays) {
1056             CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
1057             CGReleaseDisplayFadeReservation(token);
1058         }
1059
1060         return;
1061     }
1062
1063     b_in_fullscreen_transition = YES;
1064
1065     [self setAlphaValue: 0.0];
1066     [self orderFront: self];
1067     [[o_video_view window] orderFront: self];
1068
1069     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
1070     [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
1071
1072     if (o_fullscreen_anim1) {
1073         [o_fullscreen_anim1 stopAnimation];
1074         [o_fullscreen_anim1 release];
1075     }
1076     if (o_fullscreen_anim2) {
1077         [o_fullscreen_anim2 stopAnimation];
1078         [o_fullscreen_anim2 release];
1079     }
1080
1081     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1082     frame.origin.x += [self frame].origin.x;
1083     frame.origin.y += [self frame].origin.y;
1084
1085     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1086     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
1087     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1088
1089     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1090     [dict2 release];
1091
1092     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1093     [o_fullscreen_anim2 setDuration: 0.3];
1094     [o_fullscreen_anim2 setFrameRate: 30];
1095
1096     [o_fullscreen_anim2 setDelegate: self];
1097
1098     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1099
1100     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1101     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1102     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1103
1104     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1105     [dict1 release];
1106
1107     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1108     [o_fullscreen_anim1 setDuration: 0.2];
1109     [o_fullscreen_anim1 setFrameRate: 30];
1110     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1111
1112     /* Make sure o_fullscreen_window is the frontmost window */
1113     [o_fullscreen_window orderFront: self];
1114
1115     [o_fullscreen_anim1 startAnimation];
1116     /* fullscreenAnimation will be unlocked when animation ends */
1117 }
1118
1119 - (void)hasEndedFullscreen
1120 {
1121     [self setFullscreen:NO];
1122     b_in_fullscreen_transition = NO;
1123
1124     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1125     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1126     NSDisableScreenUpdates();
1127     [o_video_view retain];
1128     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1129     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1130     [o_video_view release];
1131     [o_video_view setFrame:[o_temp_view frame]];
1132     if ([[o_video_view subviews] count] > 0)
1133         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1134
1135     [o_video_view setHidden: b_video_view_was_hidden];
1136
1137     [super makeKeyAndOrderFront:self]; /* our version (in main window) contains a workaround */
1138
1139     [o_fullscreen_window orderOut: self];
1140     NSEnableScreenUpdates();
1141
1142     [o_fullscreen_window release];
1143     o_fullscreen_window = nil;
1144
1145     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
1146     [self setLevel:i_originalLevel];
1147
1148     [self setAlphaValue: config_GetFloat(VLCIntf, "macosx-opaqueness")];
1149
1150     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
1151     if (![[VLCMain sharedInstance] activeVideoPlayback] && [self class] != [VLCMainWindow class])
1152         [self orderOut: self];
1153 }
1154
1155 - (void)animationDidEnd:(NSAnimation*)animation
1156 {
1157     NSArray *viewAnimations;
1158     if (o_makekey_anim == animation) {
1159         [o_makekey_anim release];
1160         return;
1161     }
1162     if ([animation currentValue] < 1.0)
1163         return;
1164
1165     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1166     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1167     if ([viewAnimations count] >=1 &&
1168         [[[viewAnimations objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect]) {
1169         /* Fullscreen ended */
1170         [self hasEndedFullscreen];
1171     } else
1172     /* Fullscreen started */
1173         [self hasBecomeFullscreen];
1174 }
1175
1176 - (void)orderOut:(id)sender
1177 {
1178     [super orderOut:sender];
1179
1180     /*
1181      * TODO reimplement leaveFullscreenAndFadeOut:YES, or remove code
1182      * and the hack below
1183     
1184     if (![NSStringFromClass([self class]) isEqualToString:@"VLCMainWindow"]) {
1185         [self leaveFullscreenAndFadeOut:YES];
1186     }
1187      */
1188 }
1189
1190 - (void)makeKeyAndOrderFront: (id)sender
1191 {
1192     /* Hack
1193      * when we exit fullscreen and fade out, we may endup in
1194      * having a window that is faded. We can't have it fade in unless we
1195      * animate again. */
1196
1197     if (!b_window_is_invisible) {
1198         /* Make sure we don't do it too much */
1199         [super makeKeyAndOrderFront: sender];
1200         return;
1201     }
1202
1203     [super setAlphaValue:0.0f];
1204     [super makeKeyAndOrderFront: sender];
1205
1206     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1207     [dict setObject:self forKey:NSViewAnimationTargetKey];
1208     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1209
1210     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1211     [dict release];
1212
1213     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1214     [o_makekey_anim setDuration: 0.1];
1215     [o_makekey_anim setFrameRate: 30];
1216     [o_makekey_anim setDelegate: self];
1217
1218     [o_makekey_anim startAnimation];
1219     b_window_is_invisible = NO;
1220
1221     /* fullscreenAnimation will be unlocked when animation ends */
1222 }
1223
1224
1225 #pragma mark -
1226 #pragma mark Accessibility stuff
1227
1228 - (NSArray *)accessibilityAttributeNames
1229 {
1230     if (!b_dark_interface || !o_titlebar_view)
1231         return [super accessibilityAttributeNames];
1232
1233     static NSMutableArray *attributes = nil;
1234     if (attributes == nil) {
1235         attributes = [[super accessibilityAttributeNames] mutableCopy];
1236         NSArray *appendAttributes = [NSArray arrayWithObjects:NSAccessibilitySubroleAttribute,
1237                                      NSAccessibilityCloseButtonAttribute,
1238                                      NSAccessibilityMinimizeButtonAttribute,
1239                                      NSAccessibilityZoomButtonAttribute, nil];
1240
1241         for(NSString *attribute in appendAttributes) {
1242             if (![attributes containsObject:attribute])
1243                 [attributes addObject:attribute];
1244         }
1245     }
1246     return attributes;
1247 }
1248
1249 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
1250 {
1251     if (b_dark_interface && o_titlebar_view) {
1252         VLCMainWindowTitleView *o_tbv = o_titlebar_view;
1253
1254         if ([o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute])
1255             return NSAccessibilityStandardWindowSubrole;
1256
1257         if ([o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute])
1258             return [[o_tbv closeButton] cell];
1259
1260         if ([o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute])
1261             return [[o_tbv minimizeButton] cell];
1262
1263         if ([o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute])
1264             return [[o_tbv zoomButton] cell];
1265     }
1266
1267     return [super accessibilityAttributeValue: o_attribute_name];
1268 }
1269
1270 @end