info_bubble_window.mm revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// Use of this source code is governed by a BSD-style license that can be
3afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// found in the LICENSE file.
4afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
5afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#import "chrome/browser/ui/cocoa/info_bubble_window.h"
6afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
7afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "base/basictypes.h"
8afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "base/logging.h"
9afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "base/mac/scoped_nsobject.h"
10afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "chrome/browser/chrome_notification_types.h"
11afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "content/public/browser/notification_observer.h"
12afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "content/public/browser/notification_registrar.h"
13afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#include "content/public/browser/notification_service.h"
14afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
15afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
16afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velunamespace {
17afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Veluconst CGFloat kOrderInSlideOffset = 10;
18afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Veluconst NSTimeInterval kOrderInAnimationDuration = 0.075;
19afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Veluconst NSTimeInterval kOrderOutAnimationDuration = 0.15;
20afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// The minimum representable time interval.  This can be used as the value
21afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// passed to +[NSAnimationContext setDuration:] to stop an in-progress
22afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// animation as quickly as possible.
23afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Veluconst NSTimeInterval kMinimumTimeInterval =
24afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    std::numeric_limits<NSTimeInterval>::min();
25afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}  // namespace
26afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
27afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu@interface InfoBubbleWindow (Private)
28afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)appIsTerminating;
29afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)finishCloseAfterAnimation;
3060238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu@end
31afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
3276c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu// A helper class to proxy app notifications to the window.
33a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Veluclass AppNotificationBridge : public content::NotificationObserver {
34a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu public:
357d1ca6cfa8c1440ddc095faf7f4e6c8716629838Erwan Velu  explicit AppNotificationBridge(InfoBubbleWindow* owner) : owner_(owner) {
36afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
37afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu                   content::NotificationService::AllSources());
38afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  }
39afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
40afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // Overridden from content::NotificationObserver.
41afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  virtual void Observe(
4276c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu      int type,
43e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu      const content::NotificationSource& source,
44afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu      const content::NotificationDetails& details) OVERRIDE {
45e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu    switch (type) {
46afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu      case chrome::NOTIFICATION_APP_TERMINATING:
47afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu        [owner_ appIsTerminating];
48e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu        break;
49afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu      default:
50afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu        NOTREACHED() << L"Unexpected notification";
51e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu    }
527d1ca6cfa8c1440ddc095faf7f4e6c8716629838Erwan Velu  }
537d1ca6cfa8c1440ddc095faf7f4e6c8716629838Erwan Velu
54afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu private:
55afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // The object we need to inform when we get a notification. Weak. Owns us.
56afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  InfoBubbleWindow* owner_;
57e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu
5872fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu  // Used for registering to receive notifications and automatic clean up.
59e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu  content::NotificationRegistrar registrar_;
60afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
61afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  DISALLOW_COPY_AND_ASSIGN(AppNotificationBridge);
62e1c6f2e1ed79734328949dc91aa7ce84281e8112Erwan Velu};
63afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
6460238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu// A delegate object for watching the alphaValue animation on InfoBubbleWindows.
65a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu// An InfoBubbleWindow instance cannot be the delegate for its own animation
66a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu// because CAAnimations retain their delegates, and since the InfoBubbleWindow
67a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu// retains its animations a retain loop would be formed.
68ca14375039856a8cb189a19c3381d88fe046d13dErwan Velu@interface InfoBubbleWindowCloser : NSObject {
69ca14375039856a8cb189a19c3381d88fe046d13dErwan Velu @private
70afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  InfoBubbleWindow* window_;  // Weak. Window to close.
71afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
72afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (id)initWithWindow:(InfoBubbleWindow*)window;
73a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu@end
74afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
75afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu@implementation InfoBubbleWindowCloser
7660238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu
77afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (id)initWithWindow:(InfoBubbleWindow*)window {
7860238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu  if ((self = [super init])) {
79afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    window_ = window;
8060238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu  }
81afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  return self;
8260238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu}
83afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
8460238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu// Callback for the alpha animation. Closes window_ if appropriate.
85afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag {
8660238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu  // When alpha reaches zero, close window_.
87afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if ([window_ alphaValue] == 0.0) {
88afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [window_ finishCloseAfterAnimation];
8960238f0e7c8bb689f31e3871a661741f2c5fd583Erwan Velu  }
90afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
91afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
92afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu@end
93afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
94fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu
95fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu@implementation InfoBubbleWindow
967d1ca6cfa8c1440ddc095faf7f4e6c8716629838Erwan Velu
9772fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu@synthesize allowedAnimations = allowedAnimations_;
98afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu@synthesize canBecomeKeyWindow = canBecomeKeyWindow_;
9972fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu
10072fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu- (id)initWithContentRect:(NSRect)contentRect
10172fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu                styleMask:(NSUInteger)aStyle
102d1415111eebe7a95d8de1af1ec61e80f9d534552Erwan Velu                  backing:(NSBackingStoreType)bufferingType
103afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu                    defer:(BOOL)flag {
10472fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu  if ((self = [super initWithContentRect:contentRect
10572fe3ef10c1911b6f9ec84af99ba6f97112e216bErwan Velu                               styleMask:NSBorderlessWindowMask
10676c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu                                 backing:bufferingType
10776c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu                                   defer:flag])) {
10876c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    [self setBackgroundColor:[NSColor clearColor]];
10976c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    [self setExcludedFromWindowsMenu:YES];
11076c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    [self setOpaque:NO];
111fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu    [self setHasShadow:YES];
11276c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    canBecomeKeyWindow_ = YES;
113fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu    allowedAnimations_ = info_bubble::kAnimateOrderIn |
114fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu                         info_bubble::kAnimateOrderOut;
115fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu    notificationBridge_.reset(new AppNotificationBridge(self));
116fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu
117fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu    // Start invisible. Will be made visible when ordered front.
118fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu    [self setAlphaValue:0.0];
119fdc0f3b646e417497849d4398029f780b0e5262fErwan Velu
120afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Set up alphaValue animation so that self is delegate for the animation.
121afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Setting up the delegate is required so that the
122afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // animationDidStop:finished: callback can be handled.
123afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Notice that only the alphaValue Animation is replaced in case
124afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // superclasses set up animations.
125afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    CAAnimation* alphaAnimation = [CABasicAnimation animation];
126afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    base::scoped_nsobject<InfoBubbleWindowCloser> delegate(
127afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu        [[InfoBubbleWindowCloser alloc] initWithWindow:self]);
128afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [alphaAnimation setDelegate:delegate];
129afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    NSMutableDictionary* animations =
130a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu        [NSMutableDictionary dictionaryWithDictionary:[self animations]];
131a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [animations setObject:alphaAnimation forKey:@"alphaValue"];
132afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [self setAnimations:animations];
133afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  }
134afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  return self;
135afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
136afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
137afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// According to
138afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953,
139afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// NSBorderlessWindowMask windows cannot become key or main. In this
140afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// case, this is not necessarily a desired behavior. As an example, the
141afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// bubble could have buttons.
142afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (BOOL)canBecomeKeyWindow {
143afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  return canBecomeKeyWindow_;
144afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
145afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
146afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// Lets the traffic light buttons on the browser window keep their "active"
147afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// state while an info bubble is open. Only has an effect on 10.7.
148afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (BOOL)_sharesParentKeyState {
149afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  return YES;
150afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
151afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
152afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)close {
153afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // Block the window from receiving events while it fades out.
154afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  closing_ = YES;
155afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
156afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if ((allowedAnimations_ & info_bubble::kAnimateOrderOut) == 0) {
157afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [self finishCloseAfterAnimation];
158afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  } else {
159afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Apply animations to hide self.
160afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [NSAnimationContext beginGrouping];
161afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [[NSAnimationContext currentContext]
162afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu        gtm_setDuration:kOrderOutAnimationDuration
163a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu              eventMask:NSLeftMouseUpMask];
164a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [[self animator] setAlphaValue:0.0];
165afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [NSAnimationContext endGrouping];
166afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  }
167afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
168afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
169afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// If the app is terminating but the window is still fading out, cancel the
170afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// animation and close the window to prevent it from leaking.
171afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// See http://crbug.com/37717
172afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)appIsTerminating {
173afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if ((allowedAnimations_ & info_bubble::kAnimateOrderOut) == 0)
174afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    return;  // The close has already happened with no Core Animation.
175afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
176afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // Cancel the current animation so that it closes immediately, triggering
177afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // |finishCloseAfterAnimation|.
178afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  [NSAnimationContext beginGrouping];
179afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval];
180afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  [[self animator] setAlphaValue:0.0];
181afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  [NSAnimationContext endGrouping];
182afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
183afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
184afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// Called by InfoBubbleWindowCloser when the window is to be really closed
185afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// after the fading animation is complete.
186afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)finishCloseAfterAnimation {
187afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if (closing_)
188afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [super close];
189afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
190afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
191afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// Adds animation for info bubbles being ordered to the front.
192afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)orderWindow:(NSWindowOrderingMode)orderingMode
193afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu         relativeTo:(NSInteger)otherWindowNumber {
194ca14375039856a8cb189a19c3381d88fe046d13dErwan Velu  // According to the documentation '0' is the otherWindowNumber when the window
195afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  // is ordered front.
196afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if (orderingMode == NSWindowAbove && otherWindowNumber == 0) {
197afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Order self appropriately assuming that its alpha is zero as set up
198afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // in the designated initializer.
199afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [super orderWindow:orderingMode relativeTo:otherWindowNumber];
200afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
201afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Set up frame so it can be adjust down by a few pixels.
202afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    NSRect frame = [self frame];
20376c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    NSPoint newOrigin = frame.origin;
20476c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    newOrigin.y += kOrderInSlideOffset;
20576c7c63e131a5f0068932c78b57eef51bc691810Erwan Velu    [self setFrameOrigin:newOrigin];
206afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
207afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // Apply animations to show and move self.
208afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [NSAnimationContext beginGrouping];
209afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    // The star currently triggers on mouse down, not mouse up.
210afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    NSTimeInterval duration =
211a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu        (allowedAnimations_ & info_bubble::kAnimateOrderIn)
212a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu            ? kOrderInAnimationDuration : kMinimumTimeInterval;
213a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [[NSAnimationContext currentContext]
214a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu        gtm_setDuration:duration
215a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu              eventMask:NSLeftMouseUpMask | NSLeftMouseDownMask];
216a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [[self animator] setAlphaValue:1.0];
217a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [[self animator] setFrame:frame display:YES];
218a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [NSAnimationContext endGrouping];
219a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu  } else {
220a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu    [super orderWindow:orderingMode relativeTo:otherWindowNumber];
221a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu  }
222a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu}
223a74198375fe2120fa670e2164cc5b92a06c7dbf0Erwan Velu
224afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// If the window is currently animating a close, block all UI events to the
225afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu// window.
226afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (void)sendEvent:(NSEvent*)theEvent {
227afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  if (!closing_)
228afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu    [super sendEvent:theEvent];
229afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
230afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
231afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu- (BOOL)isClosing {
232afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu  return closing_;
233afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu}
234afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu
235afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu@end
236afbbd646716a9e046bd6805f17214689d15a6ba6Erwan Velu