| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" | 5 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/mac/bundle_locations.h" | 8 #include "base/mac/bundle_locations.h" |
| 9 #include "base/mac/mac_util.h" | 9 #include "base/mac/mac_util.h" |
| 10 #include "chrome/browser/infobars/infobar_service.h" | 10 #include "chrome/browser/infobars/infobar_service.h" |
| 11 #import "chrome/browser/ui/cocoa/animatable_view.h" | 11 #import "chrome/browser/ui/cocoa/animatable_view.h" |
| 12 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 12 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
| 13 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" | 13 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" |
| 14 #import "chrome/browser/ui/cocoa/image_button_cell.h" | 14 #import "chrome/browser/ui/cocoa/image_button_cell.h" |
| 15 #include "chrome/browser/ui/cocoa/infobars/infobar.h" | 15 #include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h" |
| 16 #import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h" |
| 16 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" | 17 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h" |
| 17 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h" | 18 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h" |
| 18 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | 19 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" |
| 19 #include "grit/theme_resources.h" | 20 #include "grit/theme_resources.h" |
| 20 #include "grit/ui_resources.h" | 21 #include "grit/ui_resources.h" |
| 21 #include "ui/gfx/image/image.h" | 22 #include "ui/gfx/image/image.h" |
| 22 | 23 |
| 23 namespace { | 24 @interface InfoBarController () |
| 24 // Durations set to match the default SlideAnimation duration. | |
| 25 const float kAnimateOpenDuration = 0.12; | |
| 26 const float kAnimateCloseDuration = 0.12; | |
| 27 } | |
| 28 | |
| 29 @interface InfoBarController (PrivateMethods) | |
| 30 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. | 25 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil. |
| 31 - (void)initializeLabel; | 26 - (void)initializeLabel; |
| 32 | |
| 33 // Performs final cleanup after an animation is finished or stopped, including | |
| 34 // notifying the InfoBarDelegate that the infobar was closed and removing the | |
| 35 // infobar from its container, if necessary. | |
| 36 - (void)cleanUpAfterAnimation:(BOOL)finished; | |
| 37 | |
| 38 // Returns the point, in window coordinates, at which the apex of the infobar | |
| 39 // tip should be drawn. | |
| 40 - (NSPoint)pointForTipApex; | |
| 41 @end | 27 @end |
| 42 | 28 |
| 43 @implementation InfoBarController | 29 @implementation InfoBarController |
| 44 | 30 |
| 45 @synthesize containerController = containerController_; | 31 @synthesize containerController = containerController_; |
| 46 @synthesize delegate = delegate_; | 32 @synthesize infobar = infobar_; |
| 47 | 33 |
| 48 - (id)initWithDelegate:(InfoBarDelegate*)delegate | 34 - (id)initWithInfoBar:(InfoBarCocoa*)infobar { |
| 49 owner:(InfoBarService*)owner { | |
| 50 DCHECK(delegate); | |
| 51 if ((self = [super initWithNibName:@"InfoBar" | 35 if ((self = [super initWithNibName:@"InfoBar" |
| 52 bundle:base::mac::FrameworkBundle()])) { | 36 bundle:base::mac::FrameworkBundle()])) { |
| 53 delegate_ = delegate; | 37 DCHECK(infobar); |
| 54 owner_ = owner; | 38 infobar_ = infobar; |
| 55 } | 39 } |
| 56 return self; | 40 return self; |
| 57 } | 41 } |
| 58 | 42 |
| 59 // All infobars have an icon, so we set up the icon in the base class | 43 // All infobars have an icon, so we set up the icon in the base class |
| 60 // awakeFromNib. | 44 // awakeFromNib. |
| 61 - (void)awakeFromNib { | 45 - (void)awakeFromNib { |
| 62 DCHECK(delegate_); | |
| 63 | |
| 64 [[closeButton_ cell] setImageID:IDR_CLOSE_1 | 46 [[closeButton_ cell] setImageID:IDR_CLOSE_1 |
| 65 forButtonState:image_button_cell::kDefaultState]; | 47 forButtonState:image_button_cell::kDefaultState]; |
| 66 [[closeButton_ cell] setImageID:IDR_CLOSE_1_H | 48 [[closeButton_ cell] setImageID:IDR_CLOSE_1_H |
| 67 forButtonState:image_button_cell::kHoverState]; | 49 forButtonState:image_button_cell::kHoverState]; |
| 68 [[closeButton_ cell] setImageID:IDR_CLOSE_1_P | 50 [[closeButton_ cell] setImageID:IDR_CLOSE_1_P |
| 69 forButtonState:image_button_cell::kPressedState]; | 51 forButtonState:image_button_cell::kPressedState]; |
| 70 [[closeButton_ cell] setImageID:IDR_CLOSE_1 | 52 [[closeButton_ cell] setImageID:IDR_CLOSE_1 |
| 71 forButtonState:image_button_cell::kDisabledState]; | 53 forButtonState:image_button_cell::kDisabledState]; |
| 72 | 54 |
| 73 if (!delegate_->GetIcon().IsEmpty()) { | 55 if (![self delegate]->GetIcon().IsEmpty()) { |
| 74 [image_ setImage:delegate_->GetIcon().ToNSImage()]; | 56 [image_ setImage:[self delegate]->GetIcon().ToNSImage()]; |
| 75 } else { | 57 } else { |
| 76 // No icon, remove it from the view and grow the textfield to include the | 58 // No icon, remove it from the view and grow the textfield to include the |
| 77 // space. | 59 // space. |
| 78 NSRect imageFrame = [image_ frame]; | 60 NSRect imageFrame = [image_ frame]; |
| 79 NSRect labelFrame = [labelPlaceholder_ frame]; | 61 NSRect labelFrame = [labelPlaceholder_ frame]; |
| 80 labelFrame.size.width += NSMinX(imageFrame) - NSMinX(labelFrame); | 62 labelFrame.size.width += NSMinX(imageFrame) - NSMinX(labelFrame); |
| 81 labelFrame.origin.x = imageFrame.origin.x; | 63 labelFrame.origin.x = imageFrame.origin.x; |
| 82 [image_ removeFromSuperview]; | 64 [image_ removeFromSuperview]; |
| 83 image_ = nil; | 65 image_ = nil; |
| 84 [labelPlaceholder_ setFrame:labelFrame]; | 66 [labelPlaceholder_ setFrame:labelFrame]; |
| 85 } | 67 } |
| 86 [self initializeLabel]; | 68 [self initializeLabel]; |
| 87 | 69 |
| 88 [self addAdditionalControls]; | 70 [self addAdditionalControls]; |
| 89 | 71 |
| 90 infoBarView_.tipApex = [self pointForTipApex]; | 72 [infoBarView_ setInfobarType:[self delegate]->GetInfoBarType()]; |
| 91 [infoBarView_ setInfobarType:delegate_->GetInfoBarType()]; | |
| 92 } | 73 } |
| 93 | 74 |
| 94 - (void)dealloc { | 75 - (void)dealloc { |
| 95 [okButton_ setTarget:nil]; | 76 [okButton_ setTarget:nil]; |
| 96 [cancelButton_ setTarget:nil]; | 77 [cancelButton_ setTarget:nil]; |
| 97 [closeButton_ setTarget:nil]; | 78 [closeButton_ setTarget:nil]; |
| 98 [super dealloc]; | 79 [super dealloc]; |
| 99 } | 80 } |
| 100 | 81 |
| 101 // Called when someone clicks on the embedded link. | 82 // Called when someone clicks on the embedded link. |
| 102 - (BOOL)textView:(NSTextView*)textView | 83 - (BOOL)textView:(NSTextView*)textView |
| 103 clickedOnLink:(id)link | 84 clickedOnLink:(id)link |
| 104 atIndex:(NSUInteger)charIndex { | 85 atIndex:(NSUInteger)charIndex { |
| 105 if ([self respondsToSelector:@selector(linkClicked)]) | 86 if ([self respondsToSelector:@selector(linkClicked)]) |
| 106 [self performSelector:@selector(linkClicked)]; | 87 [self performSelector:@selector(linkClicked)]; |
| 107 return YES; | 88 return YES; |
| 108 } | 89 } |
| 109 | 90 |
| 110 - (BOOL)isOwned { | 91 - (BOOL)isOwned { |
| 111 return !!owner_; | 92 return infobar_->OwnerCocoa() != NULL; |
| 112 } | 93 } |
| 113 | 94 |
| 114 // Called when someone clicks on the ok button. | 95 // Called when someone clicks on the ok button. |
| 115 - (void)ok:(id)sender { | 96 - (void)ok:(id)sender { |
| 116 // Subclasses must override this method if they do not hide the ok button. | 97 // Subclasses must override this method if they do not hide the ok button. |
| 117 NOTREACHED(); | 98 NOTREACHED(); |
| 118 } | 99 } |
| 119 | 100 |
| 120 // Called when someone clicks on the cancel button. | 101 // Called when someone clicks on the cancel button. |
| 121 - (void)cancel:(id)sender { | 102 - (void)cancel:(id)sender { |
| 122 // Subclasses must override this method if they do not hide the cancel button. | 103 // Subclasses must override this method if they do not hide the cancel button. |
| 123 NOTREACHED(); | 104 NOTREACHED(); |
| 124 } | 105 } |
| 125 | 106 |
| 126 // Called when someone clicks on the close button. | 107 // Called when someone clicks on the close button. |
| 127 - (void)dismiss:(id)sender { | 108 - (void)dismiss:(id)sender { |
| 128 if (![self isOwned]) | 109 if (![self isOwned]) |
| 129 return; | 110 return; |
| 130 delegate_->InfoBarDismissed(); | 111 [self delegate]->InfoBarDismissed(); |
| 131 [self removeSelf]; | 112 [self removeSelf]; |
| 132 } | 113 } |
| 133 | 114 |
| 134 - (void)removeSelf { | 115 - (void)removeSelf { |
| 135 // |owner_| should never be NULL here. If it is, then someone violated what | 116 infobar_->RemoveSelfCocoa(); |
| 136 // they were supposed to do -- e.g. a ConfirmInfoBarDelegate subclass returned | |
| 137 // true from Accept() or Cancel() even though the infobar was already closing. | |
| 138 // In the worst case, if we also switched tabs during that process, then | |
| 139 // |this| has already been destroyed. But if that's the case, then we're | |
| 140 // going to deref a garbage |this| pointer here whether we check |owner_| or | |
| 141 // not, and in other cases (where we're still closing and |this| is valid), | |
| 142 // checking |owner_| here will avoid a NULL deref. | |
| 143 if (owner_) | |
| 144 owner_->RemoveInfoBar(delegate_); | |
| 145 } | |
| 146 | |
| 147 - (AnimatableView*)animatableView { | |
| 148 return static_cast<AnimatableView*>([self view]); | |
| 149 } | |
| 150 | |
| 151 - (void)open { | |
| 152 // Simply reset the frame size to its opened size, forcing a relayout. | |
| 153 CGFloat finalHeight = [[self view] frame].size.height; | |
| 154 [[self animatableView] setHeight:finalHeight]; | |
| 155 } | |
| 156 | |
| 157 - (void)animateOpen { | |
| 158 // Force the frame size to be 0 and then start an animation. | |
| 159 NSRect frame = [[self view] frame]; | |
| 160 CGFloat finalHeight = frame.size.height; | |
| 161 frame.size.height = 0; | |
| 162 [[self view] setFrame:frame]; | |
| 163 [[self animatableView] animateToNewHeight:finalHeight | |
| 164 duration:kAnimateOpenDuration]; | |
| 165 } | |
| 166 | |
| 167 - (void)close { | |
| 168 // Stop any running animations. | |
| 169 [[self animatableView] stopAnimation]; | |
| 170 infoBarClosing_ = YES; | |
| 171 [self cleanUpAfterAnimation:YES]; | |
| 172 } | |
| 173 | |
| 174 - (void)animateClosed { | |
| 175 // Notify the container of our intentions. | |
| 176 [containerController_ willRemoveController:self]; | |
| 177 | |
| 178 // Start animating closed. We will receive a notification when the animation | |
| 179 // is done, at which point we can remove our view from the hierarchy and | |
| 180 // notify the delegate that the infobar was closed. | |
| 181 [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration]; | |
| 182 | |
| 183 // The above call may trigger an animationDidStop: notification for any | |
| 184 // currently-running animations, so do not set |infoBarClosing_| until after | |
| 185 // starting the animation. | |
| 186 infoBarClosing_ = YES; | |
| 187 } | 117 } |
| 188 | 118 |
| 189 - (void)addAdditionalControls { | 119 - (void)addAdditionalControls { |
| 190 // Default implementation does nothing. | 120 // Default implementation does nothing. |
| 191 } | 121 } |
| 192 | 122 |
| 193 - (void)infobarWillClose { | 123 - (void)infobarWillClose { |
| 194 owner_ = NULL; | |
| 195 } | 124 } |
| 196 | 125 |
| 197 - (void)removeButtons { | 126 - (void)removeButtons { |
| 198 // Extend the label all the way across. | 127 // Extend the label all the way across. |
| 199 NSRect labelFrame = [label_.get() frame]; | 128 NSRect labelFrame = [label_.get() frame]; |
| 200 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); | 129 labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame); |
| 201 [okButton_ removeFromSuperview]; | 130 [okButton_ removeFromSuperview]; |
| 202 okButton_ = nil; | 131 okButton_ = nil; |
| 203 [cancelButton_ removeFromSuperview]; | 132 [cancelButton_ removeFromSuperview]; |
| 204 cancelButton_ = nil; | 133 cancelButton_ = nil; |
| 205 [label_.get() setFrame:labelFrame]; | 134 [label_.get() setFrame:labelFrame]; |
| 206 } | 135 } |
| 207 | 136 |
| 208 - (void)setHasTip:(BOOL)hasTip { | 137 - (void)layoutArrow { |
| 209 [infoBarView_ setHasTip:hasTip]; | 138 [infoBarView_ setArrowHeight:infobar_->arrow_height()]; |
| 139 [infoBarView_ setArrowHalfWidth:infobar_->arrow_half_width()]; |
| 140 [infoBarView_ setHasTip:![containerController_ shouldSuppressTopInfoBarTip]]; |
| 141 |
| 142 // Convert from window to view coordinates. |
| 143 NSPoint point = NSMakePoint([containerController_ infobarArrowX], 0); |
| 144 point = [infoBarView_ convertPoint:point fromView:nil]; |
| 145 [infoBarView_ setArrowX:point.x]; |
| 210 } | 146 } |
| 211 | 147 |
| 212 - (void)disablePopUpMenu:(NSMenu*)menu { | 148 - (void)disablePopUpMenu:(NSMenu*)menu { |
| 213 // Remove the menu if visible. | 149 // Remove the menu if visible. |
| 214 [menu cancelTracking]; | 150 [menu cancelTracking]; |
| 215 | 151 |
| 216 // If the menu is re-opened, prevent queries to update items. | 152 // If the menu is re-opened, prevent queries to update items. |
| 217 [menu setDelegate:nil]; | 153 [menu setDelegate:nil]; |
| 218 | 154 |
| 219 // Prevent target/action messages to the controller. | 155 // Prevent target/action messages to the controller. |
| 220 for (NSMenuItem* item in [menu itemArray]) { | 156 for (NSMenuItem* item in [menu itemArray]) { |
| 221 [item setEnabled:NO]; | 157 [item setEnabled:NO]; |
| 222 [item setTarget:nil]; | 158 [item setTarget:nil]; |
| 223 } | 159 } |
| 224 } | 160 } |
| 225 | 161 |
| 226 @end | 162 - (InfoBarDelegate*)delegate { |
| 227 | 163 return infobar_->delegate(); |
| 228 @implementation InfoBarController (PrivateMethods) | 164 } |
| 229 | 165 |
| 230 - (void)initializeLabel { | 166 - (void)initializeLabel { |
| 231 // Replace the label placeholder NSTextField with the real label NSTextView. | 167 // Replace the label placeholder NSTextField with the real label NSTextView. |
| 232 // The former doesn't show links in a nice way, but the latter can't be added | 168 // The former doesn't show links in a nice way, but the latter can't be added |
| 233 // in IB without a containing scroll view, so create the NSTextView | 169 // in IB without a containing scroll view, so create the NSTextView |
| 234 // programmatically. | 170 // programmatically. |
| 235 label_.reset([[HyperlinkTextView alloc] | 171 label_.reset([[HyperlinkTextView alloc] |
| 236 initWithFrame:[labelPlaceholder_ frame]]); | 172 initWithFrame:[labelPlaceholder_ frame]]); |
| 237 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; | 173 [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]]; |
| 238 [[labelPlaceholder_ superview] | 174 [[labelPlaceholder_ superview] |
| 239 replaceSubview:labelPlaceholder_ with:label_.get()]; | 175 replaceSubview:labelPlaceholder_ with:label_.get()]; |
| 240 labelPlaceholder_ = nil; // Now released. | 176 labelPlaceholder_ = nil; // Now released. |
| 241 [label_.get() setDelegate:self]; | 177 [label_.get() setDelegate:self]; |
| 242 } | 178 } |
| 243 | 179 |
| 244 - (void)cleanUpAfterAnimation:(BOOL)finished { | |
| 245 // Don't need to do any cleanup if the bar was animating open. | |
| 246 if (!infoBarClosing_) | |
| 247 return; | |
| 248 | |
| 249 if (delegate_) { | |
| 250 delete delegate_; | |
| 251 delegate_ = NULL; | |
| 252 } | |
| 253 | |
| 254 // If the animation ran to completion, then we need to remove ourselves from | |
| 255 // the container. If the animation was interrupted, then the container will | |
| 256 // take care of removing us. | |
| 257 // TODO(rohitrao): UGH! This works for now, but should be cleaner. | |
| 258 if (finished) | |
| 259 [containerController_ removeController:self]; | |
| 260 } | |
| 261 | |
| 262 - (void)animationDidStop:(NSAnimation*)animation { | |
| 263 [self cleanUpAfterAnimation:NO]; | |
| 264 } | |
| 265 | |
| 266 - (void)animationDidEnd:(NSAnimation*)animation { | |
| 267 [self cleanUpAfterAnimation:YES]; | |
| 268 } | |
| 269 | |
| 270 - (NSPoint)pointForTipApex { | |
| 271 BrowserWindowController* windowController = | |
| 272 [containerController_ browserWindowController]; | |
| 273 if (!windowController) { | |
| 274 // This should only happen in unit tests. | |
| 275 return NSZeroPoint; | |
| 276 } | |
| 277 | |
| 278 LocationBarViewMac* locationBar = [windowController locationBarBridge]; | |
| 279 return locationBar->GetPageInfoBubblePoint(); | |
| 280 } | |
| 281 | |
| 282 @end | 180 @end |
| OLD | NEW |