]> git.sesse.net Git - vlc/blob - modules/gui/macosx/Windows.m
macosx: add workaround to avoid grey or transparent top bars in fullscreen mode
[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 ([[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]) {
580         NSRect videoWindowFrame = [self frame];
581         NSRect viewRect = [o_video_view convertRect:[o_video_view bounds] toView: nil];
582         NSRect contentRect = [self contentRectForFrameRect:videoWindowFrame];
583         float marginy = viewRect.origin.y + videoWindowFrame.size.height - contentRect.size.height;
584         float marginx = contentRect.size.width - viewRect.size.width;
585         if (o_titlebar_view && b_dark_interface)
586             marginy += [o_titlebar_view frame].size.height;
587
588         proposedFrameSize.height = (proposedFrameSize.width - marginx) * nativeVideoSize.height / nativeVideoSize.width + marginy;
589     }
590
591     return proposedFrameSize;
592 }
593
594 - (void)windowWillMiniaturize:(NSNotification *)notification
595 {
596     // Set level to normal as a workaround for Mavericks bug causing window
597     // to vanish from screen, see radar://15473716
598     i_originalLevel = [self level];
599     [self setLevel: NSNormalWindowLevel];
600 }
601
602 - (void)windowDidDeminiaturize:(NSNotification *)notification
603 {
604     [self setLevel: i_originalLevel];
605 }
606
607 #pragma mark -
608 #pragma mark Mouse cursor handling
609
610 //  NSTimer selectors require this function signature as per Apple's docs
611 - (void)hideMouseCursor:(NSTimer *)timer
612 {
613     [NSCursor setHiddenUntilMouseMoves: YES];
614 }
615
616 - (void)recreateHideMouseTimer
617 {
618     if (t_hide_mouse_timer != nil) {
619         [t_hide_mouse_timer invalidate];
620         [t_hide_mouse_timer release];
621     }
622
623     t_hide_mouse_timer = [NSTimer scheduledTimerWithTimeInterval:2
624                                                           target:self
625                                                         selector:@selector(hideMouseCursor:)
626                                                         userInfo:nil
627                                                          repeats:NO];
628     [t_hide_mouse_timer retain];
629 }
630
631 //  Called automatically if window's acceptsMouseMovedEvents property is true
632 - (void)mouseMoved:(NSEvent *)theEvent
633 {
634     if (b_fullscreen)
635         [self recreateHideMouseTimer];
636
637     [super mouseMoved: theEvent];
638 }
639
640 #pragma mark -
641 #pragma mark Lion native fullscreen handling
642
643 - (void)becomeKeyWindow
644 {
645     [super becomeKeyWindow];
646
647     // change fspanel state for the case when multiple windows are in fullscreen
648     if ([self hasActiveVideo] && [self fullscreen])
649         [[[VLCMainWindow sharedInstance] fsPanel] setActive:nil];
650     else
651         [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
652 }
653
654 - (void)resignKeyWindow
655 {
656     [super resignKeyWindow];
657
658     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive:nil];
659 }
660
661 -(NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow *)window
662 {
663     if (window == self) {
664         return [NSArray arrayWithObject:window];
665     }
666
667     return nil;
668 }
669
670 - (NSArray*)customWindowsToExitFullScreenForWindow:(NSWindow*)window
671 {
672     if (window == self) {
673         return [NSArray arrayWithObject:window];
674     }
675
676     return nil;
677 }
678
679 - (void)window:window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration
680 {
681     [window setStyleMask:([window styleMask] | NSFullScreenWindowMask)];
682
683     NSScreen *screen = [window screen];
684     NSRect screenFrame = [screen frame];
685
686     [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
687         [context setDuration:0.5 * duration];
688         [[window animator] setFrame:screenFrame display:YES];
689     } completionHandler:nil];
690 }
691
692 - (void)window:window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration
693 {
694     [window setStyleMask:([window styleMask] & ~NSFullScreenWindowMask)];
695     [[window animator] setFrame:frameBeforeLionFullscreen display:YES animate:YES];
696
697     [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
698         [context setDuration:0.5 * duration];
699         [[window animator] setFrame:frameBeforeLionFullscreen display:YES animate:YES];
700     } completionHandler:nil];
701 }
702
703 - (void)windowWillEnterFullScreen:(NSNotification *)notification
704 {
705     // workaround, see #6668
706     [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
707
708     i_originalLevel = [self level];
709     // b_fullscreen and b_in_fullscreen_transition must not be true yet
710     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
711     [self setLevel:NSNormalWindowLevel];
712
713     b_in_fullscreen_transition = YES;
714
715     var_SetBool(pl_Get(VLCIntf), "fullscreen", true);
716
717     frameBeforeLionFullscreen = [self frame];
718
719     if ([self hasActiveVideo]) {
720         vout_thread_t *p_vout = getVoutForActiveWindow();
721         if (p_vout) {
722             var_SetBool(p_vout, "fullscreen", true);
723             vlc_object_release(p_vout);
724         }
725     }
726
727     if ([self hasActiveVideo])
728         [[VLCMainWindow sharedInstance] recreateHideMouseTimer];
729
730     if (b_dark_interface) {
731         [o_titlebar_view removeFromSuperviewWithoutNeedingDisplay];
732
733         NSRect winrect;
734         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
735         winrect = [self frame];
736
737         winrect.size.height = winrect.size.height - f_titleBarHeight;
738         [self setFrame: winrect display:NO animate:NO];
739     }
740
741     [o_video_view setFrame: [[self contentView] frame]];
742     if (![o_video_view isHidden]) {
743         [[o_controls_bar bottomBarView] setHidden: YES];
744     }
745
746     [self setMovableByWindowBackground: NO];
747 }
748
749 - (void)windowDidEnterFullScreen:(NSNotification *)notification
750 {
751     // Indeed, we somehow can have an "inactive" fullscreen (but a visible window!).
752     // But this creates some problems when leaving fs over remote intfs, so activate app here.
753     [NSApp activateIgnoringOtherApps:YES];
754
755     [self setFullscreen: YES];
756     b_in_fullscreen_transition = NO;
757
758     if ([self hasActiveVideo]) {
759         [[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: self];
760         if (![o_video_view isHidden])
761             [[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
762     }
763
764     NSArray *subviews = [[self videoView] subviews];
765     NSUInteger count = [subviews count];
766
767     for (NSUInteger x = 0; x < count; x++) {
768         if ([[subviews objectAtIndex:x] respondsToSelector:@selector(reshape)])
769             [[subviews objectAtIndex:x] reshape];
770     }
771
772 }
773
774 - (void)windowWillExitFullScreen:(NSNotification *)notification
775 {
776     b_in_fullscreen_transition = YES;
777     [self setFullscreen: NO];
778
779     var_SetBool(pl_Get(VLCIntf), "fullscreen", false);
780
781     if ([self hasActiveVideo]) {
782         vout_thread_t *p_vout = getVoutForActiveWindow();
783         if (p_vout) {
784             var_SetBool(p_vout, "fullscreen", false);
785             vlc_object_release(p_vout);
786         }
787     }
788
789     [NSCursor setHiddenUntilMouseMoves: NO];
790     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
791
792
793     if (b_dark_interface) {
794         NSRect winrect;
795         CGFloat f_titleBarHeight = [o_titlebar_view frame].size.height;
796
797         winrect = [o_video_view frame];
798         winrect.size.height -= f_titleBarHeight;
799         [o_video_view setFrame: winrect];
800
801         winrect = [self frame];
802         [o_titlebar_view setFrame: NSMakeRect(0, winrect.size.height - f_titleBarHeight,
803                                               winrect.size.width, f_titleBarHeight)];
804         [[self contentView] addSubview: o_titlebar_view];
805
806         winrect.size.height = winrect.size.height + f_titleBarHeight;
807         [self setFrame: winrect display:NO animate:NO];
808     }
809
810     NSRect videoViewFrame = [o_video_view frame];
811     videoViewFrame.origin.y += [o_controls_bar height];
812     videoViewFrame.size.height -= [o_controls_bar height];
813     [o_video_view setFrame: videoViewFrame];
814
815     if (![o_video_view isHidden]) {
816         [[o_controls_bar bottomBarView] setHidden: NO];
817     }
818
819     [self setMovableByWindowBackground: YES];
820 }
821
822 - (void)windowDidExitFullScreen:(NSNotification *)notification
823 {
824     b_in_fullscreen_transition = NO;
825
826     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
827     [self setLevel:i_originalLevel];
828 }
829
830 #pragma mark -
831 #pragma mark Fullscreen Logic
832
833 - (void)enterFullscreen
834 {
835     NSMutableDictionary *dict1, *dict2;
836     NSScreen *screen;
837     NSRect screen_rect;
838     NSRect rect;
839     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
840
841     screen = [NSScreen screenWithDisplayID:(CGDirectDisplayID)var_InheritInteger(VLCIntf, "macosx-vdev")];
842
843     if (!screen) {
844         msg_Dbg(VLCIntf, "chosen screen isn't present, using current screen for fullscreen mode");
845         screen = [self screen];
846     }
847     if (!screen) {
848         msg_Dbg(VLCIntf, "Using deepest screen");
849         screen = [NSScreen deepestScreen];
850     }
851
852     screen_rect = [screen frame];
853
854     if (o_controls_bar)
855         [o_controls_bar setFullscreenState:YES];
856     [[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:YES];
857
858     [[VLCMainWindow sharedInstance] recreateHideMouseTimer];
859
860     if (blackout_other_displays)
861         [screen blackoutOtherScreens];
862
863     /* Make sure we don't see the window flashes in float-on-top mode */
864     i_originalLevel = [self level];
865     // b_fullscreen must not be true yet
866     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: NSNormalWindowLevel];
867     [self setLevel:NSNormalWindowLevel];
868
869     /* Only create the o_fullscreen_window if we are not in the middle of the zooming animation */
870     if (!o_fullscreen_window) {
871         /* We can't change the styleMask of an already created NSWindow, so we create another window, and do eye catching stuff */
872
873         rect = [[o_video_view superview] convertRect: [o_video_view frame] toView: nil]; /* Convert to Window base coord */
874         rect.origin.x += [self frame].origin.x;
875         rect.origin.y += [self frame].origin.y;
876         o_fullscreen_window = [[VLCWindow alloc] initWithContentRect:rect styleMask: NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
877         [o_fullscreen_window setBackgroundColor: [NSColor blackColor]];
878         [o_fullscreen_window setCanBecomeKeyWindow: YES];
879         [o_fullscreen_window setCanBecomeMainWindow: YES];
880         [o_fullscreen_window setHasActiveVideo: YES];
881         [o_fullscreen_window setFullscreen: YES];
882
883         if (![self isVisible] || [self alphaValue] == 0.0) {
884             /* We don't animate if we are not visible, instead we
885              * simply fade the display */
886             CGDisplayFadeReservationToken token;
887
888             if (blackout_other_displays) {
889                 CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
890                 CGDisplayFade(token, 0.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
891             }
892
893             [screen setFullscreenPresentationOptions];
894
895             [o_video_view retain];
896             [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
897             [o_temp_view setFrame:[o_video_view frame]];
898             [o_fullscreen_window setContentView:o_video_view];
899             [o_video_view release];
900
901             [o_fullscreen_window makeKeyAndOrderFront:self];
902             [o_fullscreen_window orderFront:self animate:YES];
903
904             [o_fullscreen_window setFrame:screen_rect display:YES animate:YES];
905             [o_fullscreen_window setLevel:NSNormalWindowLevel];
906
907             if (blackout_other_displays) {
908                 CGDisplayFade(token, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
909                 CGReleaseDisplayFadeReservation(token);
910             }
911
912             /* Will release the lock */
913             [self hasBecomeFullscreen];
914
915             return;
916         }
917
918         /* Make sure video view gets visible in case the playlist was visible before */
919         b_video_view_was_hidden = [o_video_view isHidden];
920         [o_video_view setHidden: NO];
921
922         /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
923         NSDisableScreenUpdates();
924         [o_video_view retain];
925         [[o_video_view superview] replaceSubview:o_video_view with:o_temp_view];
926         [o_temp_view setFrame:[o_video_view frame]];
927         [o_fullscreen_window setContentView:o_video_view];
928         [o_video_view release];
929         [o_fullscreen_window makeKeyAndOrderFront:self];
930         NSEnableScreenUpdates();
931     }
932
933     /* We are in fullscreen (and no animation is running) */
934     if ([self fullscreen]) {
935         /* Make sure we are hidden */
936         [self orderOut: self];
937
938         return;
939     }
940
941     if (o_fullscreen_anim1) {
942         [o_fullscreen_anim1 stopAnimation];
943         [o_fullscreen_anim1 release];
944     }
945     if (o_fullscreen_anim2) {
946         [o_fullscreen_anim2 stopAnimation];
947         [o_fullscreen_anim2 release];
948     }
949
950     [screen setFullscreenPresentationOptions];
951
952     dict1 = [[NSMutableDictionary alloc] initWithCapacity:2];
953     dict2 = [[NSMutableDictionary alloc] initWithCapacity:3];
954
955     [dict1 setObject:self forKey:NSViewAnimationTargetKey];
956     [dict1 setObject:NSViewAnimationFadeOutEffect forKey:NSViewAnimationEffectKey];
957
958     [dict2 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
959     [dict2 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
960     [dict2 setObject:[NSValue valueWithRect:screen_rect] forKey:NSViewAnimationEndFrameKey];
961
962     /* Strategy with NSAnimation allocation:
963      - Keep at most 2 animation at a time
964      - leaveFullscreen/enterFullscreen are the only responsible for releasing and alloc-ing
965      */
966     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
967     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
968
969     [dict1 release];
970     [dict2 release];
971
972     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
973     [o_fullscreen_anim1 setDuration: 0.3];
974     [o_fullscreen_anim1 setFrameRate: 30];
975     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
976     [o_fullscreen_anim2 setDuration: 0.2];
977     [o_fullscreen_anim2 setFrameRate: 30];
978
979     [o_fullscreen_anim2 setDelegate: self];
980     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
981
982     [o_fullscreen_anim1 startAnimation];
983     /* fullscreenAnimation will be unlocked when animation ends */
984
985     b_in_fullscreen_transition = YES;
986 }
987
988 - (void)hasBecomeFullscreen
989 {
990     if ([[o_video_view subviews] count] > 0)
991         [o_fullscreen_window makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
992
993     [o_fullscreen_window makeKeyWindow];
994     [o_fullscreen_window setAcceptsMouseMovedEvents: YES];
995
996     /* tell the fspanel to move itself to front next time it's triggered */
997     [[[VLCMainWindow sharedInstance] fsPanel] setVoutWasUpdated: o_fullscreen_window];
998     [[[VLCMainWindow sharedInstance] fsPanel] setActive: nil];
999
1000     if ([self isVisible])
1001         [self orderOut: self];
1002
1003     b_in_fullscreen_transition = NO;
1004     [self setFullscreen:YES];
1005 }
1006
1007 - (void)leaveFullscreen
1008 {
1009     [self leaveFullscreenAndFadeOut: NO];
1010 }
1011
1012 - (void)leaveFullscreenAndFadeOut: (BOOL)fadeout
1013 {
1014     NSMutableDictionary *dict1, *dict2;
1015     NSRect frame;
1016     BOOL blackout_other_displays = var_InheritBool(VLCIntf, "macosx-black");
1017
1018     if (o_controls_bar)
1019         [o_controls_bar setFullscreenState:NO];
1020     [[[VLCMainWindow sharedInstance] controlsBar] setFullscreenState:NO];
1021
1022     /* We always try to do so */
1023     [NSScreen unblackoutScreens];
1024
1025     [[o_video_view window] makeKeyAndOrderFront: nil];
1026
1027     /* Don't do anything if o_fullscreen_window is already closed */
1028     if (!o_fullscreen_window) {
1029         return;
1030     }
1031
1032     if (fadeout) {
1033         /* We don't animate if we are not visible, instead we
1034          * simply fade the display */
1035         CGDisplayFadeReservationToken token;
1036
1037         if (blackout_other_displays) {
1038             CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token);
1039             CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, YES);
1040         }
1041
1042         [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
1043         [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
1044
1045         /* Will release the lock */
1046         [self hasEndedFullscreen];
1047
1048         /* Our window is hidden, and might be faded. We need to workaround that, so note it
1049          * here */
1050         b_window_is_invisible = YES;
1051
1052         if (blackout_other_displays) {
1053             CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, NO);
1054             CGReleaseDisplayFadeReservation(token);
1055         }
1056
1057         return;
1058     }
1059
1060     b_in_fullscreen_transition = YES;
1061
1062     [self setAlphaValue: 0.0];
1063     [self orderFront: self];
1064     [[o_video_view window] orderFront: self];
1065
1066     [[[VLCMainWindow sharedInstance] fsPanel] setNonActive: nil];
1067     [[o_fullscreen_window screen] setNonFullscreenPresentationOptions];
1068
1069     if (o_fullscreen_anim1) {
1070         [o_fullscreen_anim1 stopAnimation];
1071         [o_fullscreen_anim1 release];
1072     }
1073     if (o_fullscreen_anim2) {
1074         [o_fullscreen_anim2 stopAnimation];
1075         [o_fullscreen_anim2 release];
1076     }
1077
1078     frame = [[o_temp_view superview] convertRect: [o_temp_view frame] toView: nil]; /* Convert to Window base coord */
1079     frame.origin.x += [self frame].origin.x;
1080     frame.origin.y += [self frame].origin.y;
1081
1082     dict2 = [[NSMutableDictionary alloc] initWithCapacity:2];
1083     [dict2 setObject:self forKey:NSViewAnimationTargetKey];
1084     [dict2 setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1085
1086     o_fullscreen_anim2 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict2]];
1087     [dict2 release];
1088
1089     [o_fullscreen_anim2 setAnimationBlockingMode: NSAnimationNonblocking];
1090     [o_fullscreen_anim2 setDuration: 0.3];
1091     [o_fullscreen_anim2 setFrameRate: 30];
1092
1093     [o_fullscreen_anim2 setDelegate: self];
1094
1095     dict1 = [[NSMutableDictionary alloc] initWithCapacity:3];
1096
1097     [dict1 setObject:o_fullscreen_window forKey:NSViewAnimationTargetKey];
1098     [dict1 setObject:[NSValue valueWithRect:[o_fullscreen_window frame]] forKey:NSViewAnimationStartFrameKey];
1099     [dict1 setObject:[NSValue valueWithRect:frame] forKey:NSViewAnimationEndFrameKey];
1100
1101     o_fullscreen_anim1 = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict1]];
1102     [dict1 release];
1103
1104     [o_fullscreen_anim1 setAnimationBlockingMode: NSAnimationNonblocking];
1105     [o_fullscreen_anim1 setDuration: 0.2];
1106     [o_fullscreen_anim1 setFrameRate: 30];
1107     [o_fullscreen_anim2 startWhenAnimation: o_fullscreen_anim1 reachesProgress: 1.0];
1108
1109     /* Make sure o_fullscreen_window is the frontmost window */
1110     [o_fullscreen_window orderFront: self];
1111
1112     [o_fullscreen_anim1 startAnimation];
1113     /* fullscreenAnimation will be unlocked when animation ends */
1114 }
1115
1116 - (void)hasEndedFullscreen
1117 {
1118     [self setFullscreen:NO];
1119     b_in_fullscreen_transition = NO;
1120
1121     /* This function is private and should be only triggered at the end of the fullscreen change animation */
1122     /* Make sure we don't see the o_video_view disappearing of the screen during this operation */
1123     NSDisableScreenUpdates();
1124     [o_video_view retain];
1125     [o_video_view removeFromSuperviewWithoutNeedingDisplay];
1126     [[o_temp_view superview] replaceSubview:o_temp_view with:o_video_view];
1127     [o_video_view release];
1128     [o_video_view setFrame:[o_temp_view frame]];
1129     if ([[o_video_view subviews] count] > 0)
1130         [self makeFirstResponder: [[o_video_view subviews] objectAtIndex:0]];
1131
1132     [o_video_view setHidden: b_video_view_was_hidden];
1133
1134     [super makeKeyAndOrderFront:self]; /* our version (in main window) contains a workaround */
1135
1136     [o_fullscreen_window orderOut: self];
1137     NSEnableScreenUpdates();
1138
1139     [o_fullscreen_window release];
1140     o_fullscreen_window = nil;
1141
1142     [[[VLCMain sharedInstance] voutController] updateWindowLevelForHelperWindows: i_originalLevel];
1143     [self setLevel:i_originalLevel];
1144
1145     [self setAlphaValue: config_GetFloat(VLCIntf, "macosx-opaqueness")];
1146
1147     // if we quit fullscreen because there is no video anymore, make sure non-embedded window is not visible
1148     if (![[VLCMain sharedInstance] activeVideoPlayback] && [self class] != [VLCMainWindow class])
1149         [self orderOut: self];
1150 }
1151
1152 - (void)animationDidEnd:(NSAnimation*)animation
1153 {
1154     NSArray *viewAnimations;
1155     if (o_makekey_anim == animation) {
1156         [o_makekey_anim release];
1157         return;
1158     }
1159     if ([animation currentValue] < 1.0)
1160         return;
1161
1162     /* Fullscreen ended or started (we are a delegate only for leaveFullscreen's/enterFullscren's anim2) */
1163     viewAnimations = [o_fullscreen_anim2 viewAnimations];
1164     if ([viewAnimations count] >=1 &&
1165         [[[viewAnimations objectAtIndex:0] objectForKey: NSViewAnimationEffectKey] isEqualToString:NSViewAnimationFadeInEffect]) {
1166         /* Fullscreen ended */
1167         [self hasEndedFullscreen];
1168     } else
1169     /* Fullscreen started */
1170         [self hasBecomeFullscreen];
1171 }
1172
1173 - (void)orderOut:(id)sender
1174 {
1175     [super orderOut:sender];
1176
1177     /*
1178      * TODO reimplement leaveFullscreenAndFadeOut:YES, or remove code
1179      * and the hack below
1180     
1181     if (![NSStringFromClass([self class]) isEqualToString:@"VLCMainWindow"]) {
1182         [self leaveFullscreenAndFadeOut:YES];
1183     }
1184      */
1185 }
1186
1187 - (void)makeKeyAndOrderFront: (id)sender
1188 {
1189     /* Hack
1190      * when we exit fullscreen and fade out, we may endup in
1191      * having a window that is faded. We can't have it fade in unless we
1192      * animate again. */
1193
1194     if (!b_window_is_invisible) {
1195         /* Make sure we don't do it too much */
1196         [super makeKeyAndOrderFront: sender];
1197         return;
1198     }
1199
1200     [super setAlphaValue:0.0f];
1201     [super makeKeyAndOrderFront: sender];
1202
1203     NSMutableDictionary * dict = [[NSMutableDictionary alloc] initWithCapacity:2];
1204     [dict setObject:self forKey:NSViewAnimationTargetKey];
1205     [dict setObject:NSViewAnimationFadeInEffect forKey:NSViewAnimationEffectKey];
1206
1207     o_makekey_anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dict]];
1208     [dict release];
1209
1210     [o_makekey_anim setAnimationBlockingMode: NSAnimationNonblocking];
1211     [o_makekey_anim setDuration: 0.1];
1212     [o_makekey_anim setFrameRate: 30];
1213     [o_makekey_anim setDelegate: self];
1214
1215     [o_makekey_anim startAnimation];
1216     b_window_is_invisible = NO;
1217
1218     /* fullscreenAnimation will be unlocked when animation ends */
1219 }
1220
1221
1222 #pragma mark -
1223 #pragma mark Accessibility stuff
1224
1225 - (NSArray *)accessibilityAttributeNames
1226 {
1227     if (!b_dark_interface || !o_titlebar_view)
1228         return [super accessibilityAttributeNames];
1229
1230     static NSMutableArray *attributes = nil;
1231     if (attributes == nil) {
1232         attributes = [[super accessibilityAttributeNames] mutableCopy];
1233         NSArray *appendAttributes = [NSArray arrayWithObjects:NSAccessibilitySubroleAttribute,
1234                                      NSAccessibilityCloseButtonAttribute,
1235                                      NSAccessibilityMinimizeButtonAttribute,
1236                                      NSAccessibilityZoomButtonAttribute, nil];
1237
1238         for(NSString *attribute in appendAttributes) {
1239             if (![attributes containsObject:attribute])
1240                 [attributes addObject:attribute];
1241         }
1242     }
1243     return attributes;
1244 }
1245
1246 - (id)accessibilityAttributeValue: (NSString*)o_attribute_name
1247 {
1248     if (b_dark_interface && o_titlebar_view) {
1249         VLCMainWindowTitleView *o_tbv = o_titlebar_view;
1250
1251         if ([o_attribute_name isEqualTo: NSAccessibilitySubroleAttribute])
1252             return NSAccessibilityStandardWindowSubrole;
1253
1254         if ([o_attribute_name isEqualTo: NSAccessibilityCloseButtonAttribute])
1255             return [[o_tbv closeButton] cell];
1256
1257         if ([o_attribute_name isEqualTo: NSAccessibilityMinimizeButtonAttribute])
1258             return [[o_tbv minimizeButton] cell];
1259
1260         if ([o_attribute_name isEqualTo: NSAccessibilityZoomButtonAttribute])
1261             return [[o_tbv zoomButton] cell];
1262     }
1263
1264     return [super accessibilityAttributeValue: o_attribute_name];
1265 }
1266
1267 @end