| Index: chrome/browser/ui/cocoa/infobar_controller.mm
|
| ===================================================================
|
| --- chrome/browser/ui/cocoa/infobar_controller.mm (revision 71658)
|
| +++ chrome/browser/ui/cocoa/infobar_controller.mm (working copy)
|
| @@ -1,537 +0,0 @@
|
| -// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#import <Cocoa/Cocoa.h>
|
| -
|
| -#include "base/logging.h" // for NOTREACHED()
|
| -#include "base/mac/mac_util.h"
|
| -#include "base/sys_string_conversions.h"
|
| -#include "chrome/browser/tab_contents/infobar_delegate.h"
|
| -#import "chrome/browser/ui/cocoa/animatable_view.h"
|
| -#include "chrome/browser/ui/cocoa/event_utils.h"
|
| -#include "chrome/browser/ui/cocoa/infobar.h"
|
| -#import "chrome/browser/ui/cocoa/infobar_container_controller.h"
|
| -#import "chrome/browser/ui/cocoa/infobar_controller.h"
|
| -#include "skia/ext/skia_utils_mac.h"
|
| -#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
|
| -#include "webkit/glue/window_open_disposition.h"
|
| -
|
| -namespace {
|
| -// Durations set to match the default SlideAnimation duration.
|
| -const float kAnimateOpenDuration = 0.12;
|
| -const float kAnimateCloseDuration = 0.12;
|
| -}
|
| -
|
| -// This simple subclass of |NSTextView| just doesn't show the (text) cursor
|
| -// (|NSTextView| displays the cursor with full keyboard accessibility enabled).
|
| -@interface InfoBarTextView : NSTextView
|
| -- (void)fixupCursor;
|
| -@end
|
| -
|
| -@implementation InfoBarTextView
|
| -
|
| -// Never draw the insertion point (otherwise, it shows up without any user
|
| -// action if full keyboard accessibility is enabled).
|
| -- (BOOL)shouldDrawInsertionPoint {
|
| - return NO;
|
| -}
|
| -
|
| -- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
|
| - granularity:(NSSelectionGranularity)granularity {
|
| - // Do not allow selections.
|
| - return NSMakeRange(0, 0);
|
| -}
|
| -
|
| -// Convince NSTextView to not show an I-Beam cursor when the cursor is over the
|
| -// text view but not over actual text.
|
| -//
|
| -// http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html
|
| -// "NSTextView sets the cursor over itself dynamically, based on considerations
|
| -// including the text under the cursor. It does so in -mouseEntered:,
|
| -// -mouseMoved:, and -cursorUpdate:, so those would be points to consider
|
| -// overriding."
|
| -- (void)mouseMoved:(NSEvent*)e {
|
| - [super mouseMoved:e];
|
| - [self fixupCursor];
|
| -}
|
| -
|
| -- (void)mouseEntered:(NSEvent*)e {
|
| - [super mouseEntered:e];
|
| - [self fixupCursor];
|
| -}
|
| -
|
| -- (void)cursorUpdate:(NSEvent*)e {
|
| - [super cursorUpdate:e];
|
| - [self fixupCursor];
|
| -}
|
| -
|
| -- (void)fixupCursor {
|
| - if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]])
|
| - [[NSCursor arrowCursor] set];
|
| -}
|
| -
|
| -@end
|
| -
|
| -@interface InfoBarController (PrivateMethods)
|
| -// Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil.
|
| -- (void)initializeLabel;
|
| -
|
| -// Asks the container controller to remove the infobar for this delegate. This
|
| -// call will trigger a notification that starts the infobar animating closed.
|
| -- (void)removeInfoBar;
|
| -
|
| -// Performs final cleanup after an animation is finished or stopped, including
|
| -// notifying the InfoBarDelegate that the infobar was closed and removing the
|
| -// infobar from its container, if necessary.
|
| -- (void)cleanUpAfterAnimation:(BOOL)finished;
|
| -
|
| -// Sets the info bar message to the specified |message|, with a hypertext
|
| -// style link. |link| will be inserted into message at |linkOffset|.
|
| -- (void)setLabelToMessage:(NSString*)message
|
| - withLink:(NSString*)link
|
| - atOffset:(NSUInteger)linkOffset;
|
| -@end
|
| -
|
| -@implementation InfoBarController
|
| -
|
| -@synthesize containerController = containerController_;
|
| -@synthesize delegate = delegate_;
|
| -
|
| -- (id)initWithDelegate:(InfoBarDelegate*)delegate {
|
| - DCHECK(delegate);
|
| - if ((self = [super initWithNibName:@"InfoBar"
|
| - bundle:base::mac::MainAppBundle()])) {
|
| - delegate_ = delegate;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -// All infobars have an icon, so we set up the icon in the base class
|
| -// awakeFromNib.
|
| -- (void)awakeFromNib {
|
| - DCHECK(delegate_);
|
| - if (delegate_->GetIcon()) {
|
| - [image_ setImage:gfx::SkBitmapToNSImage(*(delegate_->GetIcon()))];
|
| - } else {
|
| - // No icon, remove it from the view and grow the textfield to include the
|
| - // space.
|
| - NSRect imageFrame = [image_ frame];
|
| - NSRect labelFrame = [labelPlaceholder_ frame];
|
| - labelFrame.size.width += NSMinX(imageFrame) - NSMinX(labelFrame);
|
| - labelFrame.origin.x = imageFrame.origin.x;
|
| - [image_ removeFromSuperview];
|
| - [labelPlaceholder_ setFrame:labelFrame];
|
| - }
|
| - [self initializeLabel];
|
| -
|
| - [self addAdditionalControls];
|
| -}
|
| -
|
| -// Called when someone clicks on the embedded link.
|
| -- (BOOL) textView:(NSTextView*)textView
|
| - clickedOnLink:(id)link
|
| - atIndex:(NSUInteger)charIndex {
|
| - if ([self respondsToSelector:@selector(linkClicked)])
|
| - [self performSelector:@selector(linkClicked)];
|
| - return YES;
|
| -}
|
| -
|
| -// Called when someone clicks on the ok button.
|
| -- (void)ok:(id)sender {
|
| - // Subclasses must override this method if they do not hide the ok button.
|
| - NOTREACHED();
|
| -}
|
| -
|
| -// Called when someone clicks on the cancel button.
|
| -- (void)cancel:(id)sender {
|
| - // Subclasses must override this method if they do not hide the cancel button.
|
| - NOTREACHED();
|
| -}
|
| -
|
| -// Called when someone clicks on the close button.
|
| -- (void)dismiss:(id)sender {
|
| - [self removeInfoBar];
|
| -}
|
| -
|
| -- (AnimatableView*)animatableView {
|
| - return static_cast<AnimatableView*>([self view]);
|
| -}
|
| -
|
| -- (void)open {
|
| - // Simply reset the frame size to its opened size, forcing a relayout.
|
| - CGFloat finalHeight = [[self view] frame].size.height;
|
| - [[self animatableView] setHeight:finalHeight];
|
| -}
|
| -
|
| -- (void)animateOpen {
|
| - // Force the frame size to be 0 and then start an animation.
|
| - NSRect frame = [[self view] frame];
|
| - CGFloat finalHeight = frame.size.height;
|
| - frame.size.height = 0;
|
| - [[self view] setFrame:frame];
|
| - [[self animatableView] animateToNewHeight:finalHeight
|
| - duration:kAnimateOpenDuration];
|
| -}
|
| -
|
| -- (void)close {
|
| - // Stop any running animations.
|
| - [[self animatableView] stopAnimation];
|
| - infoBarClosing_ = YES;
|
| - [self cleanUpAfterAnimation:YES];
|
| -}
|
| -
|
| -- (void)animateClosed {
|
| - // Start animating closed. We will receive a notification when the animation
|
| - // is done, at which point we can remove our view from the hierarchy and
|
| - // notify the delegate that the infobar was closed.
|
| - [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration];
|
| -
|
| - // The above call may trigger an animationDidStop: notification for any
|
| - // currently-running animations, so do not set |infoBarClosing_| until after
|
| - // starting the animation.
|
| - infoBarClosing_ = YES;
|
| -}
|
| -
|
| -- (void)addAdditionalControls {
|
| - // Default implementation does nothing.
|
| -}
|
| -
|
| -- (void)infobarWillClose {
|
| - // Default implementation does nothing.
|
| -}
|
| -
|
| -- (void)setLabelToMessage:(NSString*)message {
|
| - NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
|
| - NSFont* font = [NSFont labelFontOfSize:
|
| - [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
| - [attributes setObject:font
|
| - forKey:NSFontAttributeName];
|
| - [attributes setObject:[NSCursor arrowCursor]
|
| - forKey:NSCursorAttributeName];
|
| - scoped_nsobject<NSAttributedString> attributedString(
|
| - [[NSAttributedString alloc] initWithString:message
|
| - attributes:attributes]);
|
| - [[label_.get() textStorage] setAttributedString:attributedString];
|
| -}
|
| -
|
| -- (void)removeButtons {
|
| - // Extend the label all the way across.
|
| - NSRect labelFrame = [label_.get() frame];
|
| - labelFrame.size.width = NSMaxX([cancelButton_ frame]) - NSMinX(labelFrame);
|
| - [okButton_ removeFromSuperview];
|
| - [cancelButton_ removeFromSuperview];
|
| - [label_.get() setFrame:labelFrame];
|
| -}
|
| -
|
| -@end
|
| -
|
| -@implementation InfoBarController (PrivateMethods)
|
| -
|
| -- (void)initializeLabel {
|
| - // Replace the label placeholder NSTextField with the real label NSTextView.
|
| - // The former doesn't show links in a nice way, but the latter can't be added
|
| - // in IB without a containing scroll view, so create the NSTextView
|
| - // programmatically.
|
| - label_.reset([[InfoBarTextView alloc]
|
| - initWithFrame:[labelPlaceholder_ frame]]);
|
| - [label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]];
|
| - [[labelPlaceholder_ superview]
|
| - replaceSubview:labelPlaceholder_ with:label_.get()];
|
| - labelPlaceholder_ = nil; // Now released.
|
| - [label_.get() setDelegate:self];
|
| - [label_.get() setEditable:NO];
|
| - [label_.get() setDrawsBackground:NO];
|
| - [label_.get() setHorizontallyResizable:NO];
|
| - [label_.get() setVerticallyResizable:NO];
|
| -}
|
| -
|
| -- (void)removeInfoBar {
|
| - // TODO(rohitrao): This method can be called even if the infobar has already
|
| - // been removed and |delegate_| is NULL. Is there a way to rewrite the code
|
| - // so that inner event loops don't cause us to try and remove the infobar
|
| - // twice? http://crbug.com/54253
|
| - [containerController_ removeDelegate:delegate_];
|
| -}
|
| -
|
| -- (void)cleanUpAfterAnimation:(BOOL)finished {
|
| - // Don't need to do any cleanup if the bar was animating open.
|
| - if (!infoBarClosing_)
|
| - return;
|
| -
|
| - // Notify the delegate that the infobar was closed. The delegate may delete
|
| - // itself as a result of InfoBarClosed(), so we null out its pointer.
|
| - if (delegate_) {
|
| - delegate_->InfoBarClosed();
|
| - delegate_ = NULL;
|
| - }
|
| -
|
| - // If the animation ran to completion, then we need to remove ourselves from
|
| - // the container. If the animation was interrupted, then the container will
|
| - // take care of removing us.
|
| - // TODO(rohitrao): UGH! This works for now, but should be cleaner.
|
| - if (finished)
|
| - [containerController_ removeController:self];
|
| -}
|
| -
|
| -- (void)animationDidStop:(NSAnimation*)animation {
|
| - [self cleanUpAfterAnimation:NO];
|
| -}
|
| -
|
| -- (void)animationDidEnd:(NSAnimation*)animation {
|
| - [self cleanUpAfterAnimation:YES];
|
| -}
|
| -
|
| -// TODO(joth): This method factors out some common functionality between the
|
| -// various derived infobar classes, however the class hierarchy itself could
|
| -// use refactoring to reduce this duplication. http://crbug.com/38924
|
| -- (void)setLabelToMessage:(NSString*)message
|
| - withLink:(NSString*)link
|
| - atOffset:(NSUInteger)linkOffset {
|
| - if (linkOffset == std::wstring::npos) {
|
| - // linkOffset == std::wstring::npos means the link should be right-aligned,
|
| - // which is not supported on Mac (http://crbug.com/47728).
|
| - NOTIMPLEMENTED();
|
| - linkOffset = [message length];
|
| - }
|
| - // Create an attributes dictionary for the entire message. We have
|
| - // to expicitly set the font the control's font. We also override
|
| - // the cursor to give us the normal cursor rather than the text
|
| - // insertion cursor.
|
| - NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary];
|
| - [linkAttributes setObject:[NSCursor arrowCursor]
|
| - forKey:NSCursorAttributeName];
|
| - NSFont* font = [NSFont labelFontOfSize:
|
| - [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
| - [linkAttributes setObject:font
|
| - forKey:NSFontAttributeName];
|
| -
|
| - // Create the attributed string for the main message text.
|
| - scoped_nsobject<NSMutableAttributedString> infoText(
|
| - [[NSMutableAttributedString alloc] initWithString:message]);
|
| - [infoText.get() addAttributes:linkAttributes
|
| - range:NSMakeRange(0, [infoText.get() length])];
|
| - // Add additional attributes to style the link text appropriately as
|
| - // well as linkify it.
|
| - [linkAttributes setObject:[NSColor blueColor]
|
| - forKey:NSForegroundColorAttributeName];
|
| - [linkAttributes setObject:[NSNumber numberWithBool:YES]
|
| - forKey:NSUnderlineStyleAttributeName];
|
| - [linkAttributes setObject:[NSCursor pointingHandCursor]
|
| - forKey:NSCursorAttributeName];
|
| - [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle]
|
| - forKey:NSUnderlineStyleAttributeName];
|
| - [linkAttributes setObject:[NSString string] // dummy value
|
| - forKey:NSLinkAttributeName];
|
| -
|
| - // Insert the link text into the string at the appropriate offset.
|
| - scoped_nsobject<NSAttributedString> attributedString(
|
| - [[NSAttributedString alloc] initWithString:link
|
| - attributes:linkAttributes]);
|
| - [infoText.get() insertAttributedString:attributedString.get()
|
| - atIndex:linkOffset];
|
| - // Update the label view with the new text.
|
| - [[label_.get() textStorage] setAttributedString:infoText];
|
| -}
|
| -
|
| -@end
|
| -
|
| -
|
| -/////////////////////////////////////////////////////////////////////////
|
| -// AlertInfoBarController implementation
|
| -
|
| -@implementation AlertInfoBarController
|
| -
|
| -// Alert infobars have a text message.
|
| -- (void)addAdditionalControls {
|
| - // No buttons.
|
| - [self removeButtons];
|
| -
|
| - // Insert the text.
|
| - AlertInfoBarDelegate* delegate = delegate_->AsAlertInfoBarDelegate();
|
| - DCHECK(delegate);
|
| - [self setLabelToMessage:base::SysUTF16ToNSString(delegate->GetMessageText())];
|
| -}
|
| -
|
| -@end
|
| -
|
| -
|
| -/////////////////////////////////////////////////////////////////////////
|
| -// LinkInfoBarController implementation
|
| -
|
| -@implementation LinkInfoBarController
|
| -
|
| -// Link infobars have a text message, of which part is linkified. We
|
| -// use an NSAttributedString to display styled text, and we set a
|
| -// NSLink attribute on the hyperlink portion of the message. Infobars
|
| -// use a custom NSTextField subclass, which allows us to override
|
| -// textView:clickedOnLink:atIndex: and intercept clicks.
|
| -//
|
| -- (void)addAdditionalControls {
|
| - // No buttons.
|
| - [self removeButtons];
|
| -
|
| - LinkInfoBarDelegate* delegate = delegate_->AsLinkInfoBarDelegate();
|
| - DCHECK(delegate);
|
| - size_t offset = std::wstring::npos;
|
| - string16 message = delegate->GetMessageTextWithOffset(&offset);
|
| - [self setLabelToMessage:base::SysUTF16ToNSString(message)
|
| - withLink:base::SysUTF16ToNSString(delegate->GetLinkText())
|
| - atOffset:offset];
|
| -}
|
| -
|
| -// Called when someone clicks on the link in the infobar. This method
|
| -// is called by the InfobarTextField on its delegate (the
|
| -// LinkInfoBarController).
|
| -- (void)linkClicked {
|
| - WindowOpenDisposition disposition =
|
| - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
|
| - if (delegate_ && delegate_->AsLinkInfoBarDelegate()->LinkClicked(disposition))
|
| - [self removeInfoBar];
|
| -}
|
| -
|
| -@end
|
| -
|
| -
|
| -/////////////////////////////////////////////////////////////////////////
|
| -// ConfirmInfoBarController implementation
|
| -
|
| -@implementation ConfirmInfoBarController
|
| -
|
| -// Called when someone clicks on the "OK" button.
|
| -- (IBAction)ok:(id)sender {
|
| - if (delegate_ && delegate_->AsConfirmInfoBarDelegate()->Accept())
|
| - [self removeInfoBar];
|
| -}
|
| -
|
| -// Called when someone clicks on the "Cancel" button.
|
| -- (IBAction)cancel:(id)sender {
|
| - if (delegate_ && delegate_->AsConfirmInfoBarDelegate()->Cancel())
|
| - [self removeInfoBar];
|
| -}
|
| -
|
| -// Confirm infobars can have OK and/or cancel buttons, depending on
|
| -// the return value of GetButtons(). We create each button if
|
| -// required and position them to the left of the close button.
|
| -- (void)addAdditionalControls {
|
| - ConfirmInfoBarDelegate* delegate = delegate_->AsConfirmInfoBarDelegate();
|
| - DCHECK(delegate);
|
| - int visibleButtons = delegate->GetButtons();
|
| -
|
| - NSRect okButtonFrame = [okButton_ frame];
|
| - NSRect cancelButtonFrame = [cancelButton_ frame];
|
| -
|
| - DCHECK(NSMaxX(okButtonFrame) < NSMinX(cancelButtonFrame))
|
| - << "Cancel button expected to be on the right of the Ok button in nib";
|
| -
|
| - CGFloat rightEdge = NSMaxX(cancelButtonFrame);
|
| - CGFloat spaceBetweenButtons =
|
| - NSMinX(cancelButtonFrame) - NSMaxX(okButtonFrame);
|
| - CGFloat spaceBeforeButtons =
|
| - NSMinX(okButtonFrame) - NSMaxX([label_.get() frame]);
|
| -
|
| - // Update and position the Cancel button if needed. Otherwise, hide it.
|
| - if (visibleButtons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
|
| - [cancelButton_ setTitle:base::SysUTF16ToNSString(
|
| - delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_CANCEL))];
|
| - [GTMUILocalizerAndLayoutTweaker sizeToFitView:cancelButton_];
|
| - cancelButtonFrame = [cancelButton_ frame];
|
| -
|
| - // Position the cancel button to the left of the Close button.
|
| - cancelButtonFrame.origin.x = rightEdge - cancelButtonFrame.size.width;
|
| - [cancelButton_ setFrame:cancelButtonFrame];
|
| -
|
| - // Update the rightEdge
|
| - rightEdge = NSMinX(cancelButtonFrame);
|
| - } else {
|
| - [cancelButton_ removeFromSuperview];
|
| - }
|
| -
|
| - // Update and position the OK button if needed. Otherwise, hide it.
|
| - if (visibleButtons & ConfirmInfoBarDelegate::BUTTON_OK) {
|
| - [okButton_ setTitle:base::SysUTF16ToNSString(
|
| - delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK))];
|
| - [GTMUILocalizerAndLayoutTweaker sizeToFitView:okButton_];
|
| - okButtonFrame = [okButton_ frame];
|
| -
|
| - // If we had a Cancel button, leave space between the buttons.
|
| - if (visibleButtons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
|
| - rightEdge -= spaceBetweenButtons;
|
| - }
|
| -
|
| - // Position the OK button on our current right edge.
|
| - okButtonFrame.origin.x = rightEdge - okButtonFrame.size.width;
|
| - [okButton_ setFrame:okButtonFrame];
|
| -
|
| -
|
| - // Update the rightEdge
|
| - rightEdge = NSMinX(okButtonFrame);
|
| - } else {
|
| - [okButton_ removeFromSuperview];
|
| - }
|
| -
|
| - // If we had either button, leave space before the edge of the textfield.
|
| - if ((visibleButtons & ConfirmInfoBarDelegate::BUTTON_CANCEL) ||
|
| - (visibleButtons & ConfirmInfoBarDelegate::BUTTON_OK)) {
|
| - rightEdge -= spaceBeforeButtons;
|
| - }
|
| -
|
| - NSRect frame = [label_.get() frame];
|
| - DCHECK(rightEdge > NSMinX(frame))
|
| - << "Need to make the xib larger to handle buttons with text this long";
|
| - frame.size.width = rightEdge - NSMinX(frame);
|
| - [label_.get() setFrame:frame];
|
| -
|
| - // Set the text and link.
|
| - NSString* message = base::SysUTF16ToNSString(delegate->GetMessageText());
|
| - string16 link = delegate->GetLinkText();
|
| - if (link.empty()) {
|
| - // Simple case: no link, so just set the message directly.
|
| - [self setLabelToMessage:message];
|
| - } else {
|
| - // Inserting the link unintentionally causes the text to have a slightly
|
| - // different result to the simple case above: text is truncated on word
|
| - // boundaries (if needed) rather than elided with ellipses.
|
| -
|
| - // Add spacing between the label and the link.
|
| - message = [message stringByAppendingString:@" "];
|
| - [self setLabelToMessage:message
|
| - withLink:base::SysUTF16ToNSString(link)
|
| - atOffset:[message length]];
|
| - }
|
| -}
|
| -
|
| -// Called when someone clicks on the link in the infobar. This method
|
| -// is called by the InfobarTextField on its delegate (the
|
| -// LinkInfoBarController).
|
| -- (void)linkClicked {
|
| - WindowOpenDisposition disposition =
|
| - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
|
| - if (delegate_ &&
|
| - delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition))
|
| - [self removeInfoBar];
|
| -}
|
| -
|
| -@end
|
| -
|
| -
|
| -//////////////////////////////////////////////////////////////////////////
|
| -// CreateInfoBar() implementations
|
| -
|
| -InfoBar* AlertInfoBarDelegate::CreateInfoBar() {
|
| - AlertInfoBarController* controller =
|
| - [[AlertInfoBarController alloc] initWithDelegate:this];
|
| - return new InfoBar(controller);
|
| -}
|
| -
|
| -InfoBar* LinkInfoBarDelegate::CreateInfoBar() {
|
| - LinkInfoBarController* controller =
|
| - [[LinkInfoBarController alloc] initWithDelegate:this];
|
| - return new InfoBar(controller);
|
| -}
|
| -
|
| -InfoBar* ConfirmInfoBarDelegate::CreateInfoBar() {
|
| - ConfirmInfoBarController* controller =
|
| - [[ConfirmInfoBarController alloc] initWithDelegate:this];
|
| - return new InfoBar(controller);
|
| -}
|
|
|