blob: a33f9a33888889e3ee24e189fb6be45b908c5e7d [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
#include <algorithm>
#include "base/mac/bundle_locations.h"
#include "base/mac/mac_util.h"
#include "base/memory/singleton.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/autocomplete/autocomplete_classifier.h"
#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/background_gradient_view.h"
#include "chrome/browser/ui/cocoa/drag_util.h"
#import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
#import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
#import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
#import "chrome/browser/ui/cocoa/gradient_button_cell.h"
#import "chrome/browser/ui/cocoa/image_button_cell.h"
#import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
#import "chrome/browser/ui/cocoa/menu_button.h"
#import "chrome/browser/ui/cocoa/toolbar/back_forward_menu_controller.h"
#import "chrome/browser/ui/cocoa/toolbar/reload_button_cocoa.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_view_cocoa.h"
#import "chrome/browser/ui/cocoa/toolbar/wrench_toolbar_button_cell.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
#import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/wrench_menu_badge_controller.h"
#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/metrics/proto/omnibox_event.pb.h"
#include "components/omnibox/autocomplete_match.h"
#include "components/search_engines/template_url_service.h"
#include "components/url_fixer/url_fixer.h"
#include "content/public/browser/web_contents.h"
#include "grit/theme_resources.h"
#import "ui/base/cocoa/menu_controller.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/rect.h"
using content::OpenURLParams;
using content::Referrer;
using content::WebContents;
namespace {
// Height of the toolbar in pixels when the bookmark bar is closed.
const CGFloat kBaseToolbarHeightNormal = 35.0;
// The minimum width of the location bar in pixels.
const CGFloat kMinimumLocationBarWidth = 100.0;
// The duration of any animation that occurs within the toolbar in seconds.
const CGFloat kAnimationDuration = 0.2;
// The amount of left padding that the wrench menu should have.
const CGFloat kWrenchMenuLeftPadding = 3.0;
} // namespace
@interface ToolbarController()
@property(assign, nonatomic) Browser* browser;
- (void)addAccessibilityDescriptions;
- (void)initCommandStatus:(CommandUpdater*)commands;
- (void)prefChanged:(const std::string&)prefName;
- (BackgroundGradientView*)backgroundGradientView;
- (void)toolbarFrameChanged;
- (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
- (void)maintainMinimumLocationBarWidth;
- (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
- (void)browserActionsContainerDragged:(NSNotification*)notification;
- (void)browserActionsContainerDragFinished:(NSNotification*)notification;
- (void)browserActionsVisibilityChanged:(NSNotification*)notification;
- (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
- (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
animate:(BOOL)animate;
@end
namespace ToolbarControllerInternal {
// A class registered for C++ notifications. This is used to detect changes in
// preferences and upgrade available notifications. Bridges the notification
// back to the ToolbarController.
class NotificationBridge : public WrenchMenuBadgeController::Delegate {
public:
explicit NotificationBridge(ToolbarController* controller)
: controller_(controller),
badge_controller_([controller browser]->profile(), this) {
}
~NotificationBridge() override {}
void UpdateBadgeSeverity() {
badge_controller_.UpdateDelegate();
}
void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
WrenchIconPainter::Severity severity,
bool animate) override {
[controller_ updateWrenchButtonSeverity:severity animate:animate];
}
void OnPreferenceChanged(const std::string& pref_name) {
[controller_ prefChanged:pref_name];
}
private:
ToolbarController* controller_; // weak, owns us
WrenchMenuBadgeController badge_controller_;
DISALLOW_COPY_AND_ASSIGN(NotificationBridge);
};
} // namespace ToolbarControllerInternal
@implementation ToolbarController
@synthesize browser = browser_;
- (id)initWithCommands:(CommandUpdater*)commands
profile:(Profile*)profile
browser:(Browser*)browser
resizeDelegate:(id<ViewResizer>)resizeDelegate
nibFileNamed:(NSString*)nibName {
DCHECK(commands && profile && [nibName length]);
if ((self = [super initWithNibName:nibName
bundle:base::mac::FrameworkBundle()])) {
commands_ = commands;
profile_ = profile;
browser_ = browser;
resizeDelegate_ = resizeDelegate;
hasToolbar_ = YES;
hasLocationBar_ = YES;
// Register for notifications about state changes for the toolbar buttons
commandObserver_.reset(new CommandObserverBridge(self, commands));
commandObserver_->ObserveCommand(IDC_BACK);
commandObserver_->ObserveCommand(IDC_FORWARD);
commandObserver_->ObserveCommand(IDC_RELOAD);
commandObserver_->ObserveCommand(IDC_HOME);
commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE);
}
return self;
}
- (id)initWithCommands:(CommandUpdater*)commands
profile:(Profile*)profile
browser:(Browser*)browser
resizeDelegate:(id<ViewResizer>)resizeDelegate {
if ((self = [self initWithCommands:commands
profile:profile
browser:browser
resizeDelegate:resizeDelegate
nibFileNamed:@"Toolbar"])) {
}
return self;
}
- (void)dealloc {
// Unset ViewIDs of toolbar elements.
// ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
// |browserActionsContainerView_| are handled by themselves.
view_id_util::UnsetID(backButton_);
view_id_util::UnsetID(forwardButton_);
view_id_util::UnsetID(homeButton_);
view_id_util::UnsetID(wrenchButton_);
// Make sure any code in the base class which assumes [self view] is
// the "parent" view continues to work.
hasToolbar_ = YES;
hasLocationBar_ = YES;
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (trackingArea_.get())
[[self view] removeTrackingArea:trackingArea_.get()];
[super dealloc];
}
// Called after the view is done loading and the outlets have been hooked up.
// Now we can hook up bridges that rely on UI objects such as the location
// bar and button state.
- (void)awakeFromNib {
[[backButton_ cell] setImageID:IDR_BACK
forButtonState:image_button_cell::kDefaultState];
[[backButton_ cell] setImageID:IDR_BACK_H
forButtonState:image_button_cell::kHoverState];
[[backButton_ cell] setImageID:IDR_BACK_P
forButtonState:image_button_cell::kPressedState];
[[backButton_ cell] setImageID:IDR_BACK_D
forButtonState:image_button_cell::kDisabledState];
[[forwardButton_ cell] setImageID:IDR_FORWARD
forButtonState:image_button_cell::kDefaultState];
[[forwardButton_ cell] setImageID:IDR_FORWARD_H
forButtonState:image_button_cell::kHoverState];
[[forwardButton_ cell] setImageID:IDR_FORWARD_P
forButtonState:image_button_cell::kPressedState];
[[forwardButton_ cell] setImageID:IDR_FORWARD_D
forButtonState:image_button_cell::kDisabledState];
[[reloadButton_ cell] setImageID:IDR_RELOAD
forButtonState:image_button_cell::kDefaultState];
[[reloadButton_ cell] setImageID:IDR_RELOAD_H
forButtonState:image_button_cell::kHoverState];
[[reloadButton_ cell] setImageID:IDR_RELOAD_P
forButtonState:image_button_cell::kPressedState];
[[homeButton_ cell] setImageID:IDR_HOME
forButtonState:image_button_cell::kDefaultState];
[[homeButton_ cell] setImageID:IDR_HOME_H
forButtonState:image_button_cell::kHoverState];
[[homeButton_ cell] setImageID:IDR_HOME_P
forButtonState:image_button_cell::kPressedState];
[[wrenchButton_ cell] setImageID:IDR_TOOLS
forButtonState:image_button_cell::kDefaultState];
[[wrenchButton_ cell] setImageID:IDR_TOOLS_H
forButtonState:image_button_cell::kHoverState];
[[wrenchButton_ cell] setImageID:IDR_TOOLS_P
forButtonState:image_button_cell::kPressedState];
notificationBridge_.reset(
new ToolbarControllerInternal::NotificationBridge(self));
notificationBridge_->UpdateBadgeSeverity();
[wrenchButton_ setOpenMenuOnClick:YES];
[backButton_ setOpenMenuOnRightClick:YES];
[forwardButton_ setOpenMenuOnRightClick:YES];
[backButton_ setHandleMiddleClick:YES];
[forwardButton_ setHandleMiddleClick:YES];
[reloadButton_ setHandleMiddleClick:YES];
[homeButton_ setHandleMiddleClick:YES];
[self initCommandStatus:commands_];
[reloadButton_ setCommandUpdater:commands_];
locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
profile_, browser_));
[locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
// Register pref observers for the optional home and page/options buttons
// and then add them to the toolbar based on those prefs.
PrefService* prefs = profile_->GetPrefs();
showHomeButton_.Init(
prefs::kShowHomeButton, prefs,
base::Bind(
&ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
base::Unretained(notificationBridge_.get())));
[self showOptionalHomeButton];
[self installWrenchMenu];
// Create the controllers for the back/forward menus.
backMenuController_.reset([[BackForwardMenuController alloc]
initWithBrowser:browser_
modelType:BACK_FORWARD_MENU_TYPE_BACK
button:backButton_]);
forwardMenuController_.reset([[BackForwardMenuController alloc]
initWithBrowser:browser_
modelType:BACK_FORWARD_MENU_TYPE_FORWARD
button:forwardButton_]);
// For a popup window, the toolbar is really just a location bar
// (see override for [ToolbarController view], below). When going
// fullscreen, we remove the toolbar controller's view from the view
// hierarchy. Calling [locationBar_ removeFromSuperview] when going
// fullscreen causes it to get released, making us unhappy
// (http://crbug.com/18551). We avoid the problem by incrementing
// the retain count of the location bar; use of the scoped object
// helps us remember to release it.
locationBarRetainer_.reset([locationBar_ retain]);
trackingArea_.reset(
[[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
options:NSTrackingMouseMoved |
NSTrackingInVisibleRect |
NSTrackingMouseEnteredAndExited |
NSTrackingActiveAlways
owner:self
userInfo:nil]);
NSView* toolbarView = [self view];
[toolbarView addTrackingArea:trackingArea_.get()];
// If the user has any Browser Actions installed, the container view for them
// may have to be resized depending on the width of the toolbar frame.
[toolbarView setPostsFrameChangedNotifications:YES];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(toolbarFrameChanged)
name:NSViewFrameDidChangeNotification
object:toolbarView];
// Set ViewIDs for toolbar elements which don't have their dedicated class.
// ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
// |browserActionsContainerView_| are handled by themselves.
view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
[self addAccessibilityDescriptions];
}
- (void)addAccessibilityDescriptions {
// Set accessibility descriptions. http://openradar.appspot.com/7496255
NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
[[backButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
[[forwardButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
[[reloadButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
[[homeButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
[[locationBar_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
[[wrenchButton_ cell]
accessibilitySetOverrideValue:description
forAttribute:NSAccessibilityDescriptionAttribute];
}
- (void)mouseExited:(NSEvent*)theEvent {
[[hoveredButton_ cell] setIsMouseInside:NO];
[hoveredButton_ release];
hoveredButton_ = nil;
}
- (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
NSButton* targetView = (NSButton*)[[self view]
hitTest:[theEvent locationInWindow]];
// Only interpret the view as a hoverButton_ if it's both button and has a
// button cell that cares. GradientButtonCell derived cells care.
if (([targetView isKindOfClass:[NSButton class]]) &&
([[targetView cell]
respondsToSelector:@selector(setIsMouseInside:)]))
return targetView;
return nil;
}
- (void)mouseMoved:(NSEvent*)theEvent {
NSButton* targetView = [self hoverButtonForEvent:theEvent];
if (hoveredButton_ != targetView) {
[[hoveredButton_ cell] setIsMouseInside:NO];
[[targetView cell] setIsMouseInside:YES];
[hoveredButton_ release];
hoveredButton_ = [targetView retain];
}
}
- (void)mouseEntered:(NSEvent*)event {
[self mouseMoved:event];
}
- (LocationBarViewMac*)locationBarBridge {
return locationBarView_.get();
}
- (void)focusLocationBar:(BOOL)selectAll {
if (locationBarView_.get()) {
if (selectAll &&
locationBarView_->GetToolbarModel()->WouldOmitURLDueToOriginChip()) {
// select_all is true when it's expected that the user may want to copy
// the URL to the clipboard. If the origin chip is being displayed (and
// thus the URL is not being shown in the Omnibox) show it now to support
// the same functionality.
locationBarView_->GetOmniboxView()->ShowURL();
} else {
locationBarView_->FocusLocation(selectAll ? true : false);
}
}
}
// Called when the state for a command changes to |enabled|. Update the
// corresponding UI element.
- (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled {
NSButton* button = nil;
switch (command) {
case IDC_BACK:
button = backButton_;
break;
case IDC_FORWARD:
button = forwardButton_;
break;
case IDC_HOME:
button = homeButton_;
break;
}
[button setEnabled:enabled];
}
// Init the enabled state of the buttons on the toolbar to match the state in
// the controller.
- (void)initCommandStatus:(CommandUpdater*)commands {
[backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
[forwardButton_
setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
[reloadButton_ setEnabled:YES];
[homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
}
- (void)updateToolbarWithContents:(WebContents*)tab {
locationBarView_->Update(tab);
[locationBar_ updateMouseTracking];
if (browserActionsController_.get()) {
[browserActionsController_ update];
}
BOOL needReloadMenu = chrome::IsDebuggerAttachedToCurrentTab(browser_);
[reloadButton_ setMenuEnabled:needReloadMenu];
}
- (void)setStarredState:(BOOL)isStarred {
locationBarView_->SetStarred(isStarred);
}
- (void)setTranslateIconLit:(BOOL)on {
locationBarView_->SetTranslateIconLit(on);
}
- (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
locationBarView_->ZoomChangedForActiveTab(
canShowBubble && ![wrenchMenuController_ isMenuOpen]);
}
- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
[reloadButton_ setIsLoading:isLoading force:force];
}
- (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
[self view]; // Force nib loading.
hasToolbar_ = toolbar;
// If there's a toolbar, there must be a location bar.
DCHECK((toolbar && locBar) || !toolbar);
hasLocationBar_ = toolbar ? YES : locBar;
// Decide whether to hide/show based on whether there's a location bar.
[[self view] setHidden:!hasLocationBar_];
// Make location bar not editable when in a pop-up.
locationBarView_->SetEditable(toolbar);
}
- (NSView*)view {
if (hasToolbar_)
return [super view];
return locationBar_;
}
// (Private) Returns the backdrop to the toolbar.
- (BackgroundGradientView*)backgroundGradientView {
// We really do mean |[super view]|; see our override of |-view|.
DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
return (BackgroundGradientView*)[super view];
}
- (id)customFieldEditorForObject:(id)obj {
if (obj == locationBar_) {
// Lazilly construct Field editor, Cocoa UI code always runs on the
// same thread, so there shoudn't be a race condition here.
if (autocompleteTextFieldEditor_.get() == nil) {
autocompleteTextFieldEditor_.reset(
[[AutocompleteTextFieldEditor alloc] init]);
}
// This needs to be called every time, otherwise notifications
// aren't sent correctly.
DCHECK(autocompleteTextFieldEditor_.get());
[autocompleteTextFieldEditor_.get() setFieldEditor:YES];
if (base::mac::IsOSSnowLeopard()) {
// Manually transferring the drawsBackground and backgroundColor
// properties is necessary to ensure anti-aliased text on 10.6.
[autocompleteTextFieldEditor_
setDrawsBackground:[locationBar_ drawsBackground]];
[autocompleteTextFieldEditor_
setBackgroundColor:[locationBar_ backgroundColor]];
}
return autocompleteTextFieldEditor_.get();
}
return nil;
}
// Returns an array of views in the order of the outlets above.
- (NSArray*)toolbarViews {
return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
homeButton_, wrenchButton_, locationBar_,
browserActionsContainerView_, nil];
}
// Moves |rect| to the right by |delta|, keeping the right side fixed by
// shrinking the width to compensate. Passing a negative value for |deltaX|
// moves to the left and increases the width.
- (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
NSRect frame = NSOffsetRect(rect, deltaX, 0);
frame.size.width -= deltaX;
return frame;
}
// Show or hide the home button based on the pref.
- (void)showOptionalHomeButton {
// Ignore this message if only showing the URL bar.
if (!hasToolbar_)
return;
BOOL hide = showHomeButton_.GetValue() ? NO : YES;
if (hide == [homeButton_ isHidden])
return; // Nothing to do, view state matches pref state.
// Always shift the text field by the width of the home button minus one pixel
// since the frame edges of each button are right on top of each other. When
// hiding the button, reverse the direction of the movement (to the left).
CGFloat moveX = [homeButton_ frame].size.width - 1.0;
if (hide)
moveX *= -1; // Reverse the direction of the move.
[locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
byAmount:moveX]];
[homeButton_ setHidden:hide];
}
// Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
// can be done every time the buttons are shown.
- (void)installWrenchMenu {
if (wrenchMenuController_.get())
return;
wrenchMenuController_.reset(
[[WrenchMenuController alloc] initWithBrowser:browser_]);
[wrenchMenuController_ setUseWithPopUpButtonCell:YES];
[wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
}
- (WrenchMenuController*)wrenchMenuController {
return wrenchMenuController_;
}
- (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
animate:(BOOL)animate {
WrenchToolbarButtonCell* cell =
base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
[cell setSeverity:severity shouldAnimate:animate];
}
- (void)prefChanged:(const std::string&)prefName {
if (prefName == prefs::kShowHomeButton) {
[self showOptionalHomeButton];
}
}
- (void)createBrowserActionButtons {
if (!browserActionsController_.get()) {
browserActionsController_.reset([[BrowserActionsController alloc]
initWithBrowser:browser_
containerView:browserActionsContainerView_]);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(browserActionsContainerDragged:)
name:kBrowserActionGrippyDraggingNotification
object:browserActionsController_];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(browserActionsContainerDragFinished:)
name:kBrowserActionGrippyDragFinishedNotification
object:browserActionsController_];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(browserActionsVisibilityChanged:)
name:kBrowserActionVisibilityChangedNotification
object:browserActionsController_];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(adjustBrowserActionsContainerForNewWindow:)
name:NSWindowDidBecomeKeyNotification
object:[[self view] window]];
}
CGFloat containerWidth = [browserActionsContainerView_ isHidden] ? 0.0 :
NSWidth([browserActionsContainerView_ frame]);
if (containerWidth > 0.0)
[self adjustLocationSizeBy:(containerWidth * -1) animate:NO];
}
- (void)adjustBrowserActionsContainerForNewWindow:
(NSNotification*)notification {
[self toolbarFrameChanged];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSWindowDidBecomeKeyNotification
object:[[self view] window]];
}
- (void)browserActionsContainerDragged:(NSNotification*)notification {
CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
[browserActionsContainerView_ setCanDragLeft:!locationBarAtMinSize_];
[browserActionsContainerView_ setGrippyPinned:locationBarAtMinSize_];
[self adjustLocationSizeBy:
[browserActionsContainerView_ resizeDeltaX] animate:NO];
}
- (void)browserActionsContainerDragFinished:(NSNotification*)notification {
[browserActionsController_ resizeContainerAndAnimate:YES];
[self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
}
- (void)browserActionsVisibilityChanged:(NSNotification*)notification {
[self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
}
- (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
CGFloat leftDistance;
if ([browserActionsContainerView_ isHidden]) {
CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
} else {
NSRect containerFrame = animate ?
[browserActionsContainerView_ animationEndFrame] :
[browserActionsContainerView_ frame];
leftDistance = containerFrame.origin.x - locationBarXPos;
}
if (leftDistance != 0.0)
[self adjustLocationSizeBy:leftDistance animate:animate];
}
- (void)maintainMinimumLocationBarWidth {
CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
if (locationBarAtMinSize_) {
CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
[self adjustLocationSizeBy:dX animate:NO];
}
}
- (void)toolbarFrameChanged {
// Do nothing if the frame changes but no Browser Action Controller is
// present.
if (!browserActionsController_.get())
return;
[self maintainMinimumLocationBarWidth];
if (locationBarAtMinSize_) {
// Once the grippy is pinned, leave it until it is explicity un-pinned.
[browserActionsContainerView_ setGrippyPinned:YES];
NSRect containerFrame = [browserActionsContainerView_ frame];
// Determine how much the container needs to move in case it's overlapping
// with the location bar.
CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
containerFrame = NSOffsetRect(containerFrame, dX, 0);
containerFrame.size.width -= dX;
[browserActionsContainerView_ setFrame:containerFrame];
} else if (!locationBarAtMinSize_ &&
[browserActionsContainerView_ grippyPinned]) {
// Expand out the container until it hits the saved size, then unpin the
// grippy.
// Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
CGFloat dX = NSWidth([locationBar_ frame]) -
(kMinimumLocationBarWidth + 0.1);
NSRect containerFrame = [browserActionsContainerView_ frame];
containerFrame = NSOffsetRect(containerFrame, -dX, 0);
containerFrame.size.width += dX;
CGFloat savedContainerWidth = [browserActionsController_ savedWidth];
if (NSWidth(containerFrame) >= savedContainerWidth) {
containerFrame = NSOffsetRect(containerFrame,
NSWidth(containerFrame) - savedContainerWidth, 0);
containerFrame.size.width = savedContainerWidth;
[browserActionsContainerView_ setGrippyPinned:NO];
}
[browserActionsContainerView_ setFrame:containerFrame];
[self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
}
}
- (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
// Ensure that the location bar is in its proper place.
NSRect locationFrame = [locationBar_ frame];
locationFrame.size.width += dX;
if (!animate) {
[locationBar_ setFrame:locationFrame];
return;
}
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:kAnimationDuration];
[[locationBar_ animator] setFrame:locationFrame];
[NSAnimationContext endGrouping];
}
- (NSPoint)bookmarkBubblePoint {
if (locationBarView_->IsStarEnabled())
return locationBarView_->GetBookmarkBubblePoint();
// Grab bottom middle of hotdogs.
NSRect frame = wrenchButton_.frame;
NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
// Inset to account for the whitespace around the hotdogs.
point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
return [self.view convertPoint:point toView:nil];
}
- (NSPoint)managePasswordsBubblePoint {
return locationBarView_->GetManagePasswordsBubblePoint();
}
- (NSPoint)translateBubblePoint {
return locationBarView_->GetTranslateBubblePoint();
}
- (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
// With no toolbar, just ignore the compression.
if (!hasToolbar_)
return NSHeight([locationBar_ frame]);
return kBaseToolbarHeightNormal - compressByHeight;
}
- (void)setDividerOpacity:(CGFloat)opacity {
BackgroundGradientView* view = [self backgroundGradientView];
[view setShowsDivider:(opacity > 0 ? YES : NO)];
// We may not have a toolbar view (e.g., popup windows only have a location
// bar).
if ([view isKindOfClass:[ToolbarView class]]) {
ToolbarView* toolbarView = (ToolbarView*)view;
[toolbarView setDividerOpacity:opacity];
}
[view setNeedsDisplay:YES];
}
- (BrowserActionsController*)browserActionsController {
return browserActionsController_.get();
}
- (NSView*)wrenchButton {
return wrenchButton_;
}
// (URLDropTargetController protocol)
- (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
// TODO(viettrungluu): This code is more or less copied from the code in
// |TabStripController|. I'll refactor this soon to make it common and expand
// its capabilities (e.g., allow text DnD).
if ([urls count] < 1) {
NOTREACHED();
return;
}
// TODO(viettrungluu): dropping multiple URLs?
if ([urls count] > 1)
NOTIMPLEMENTED();
// Get the first URL and fix it up.
GURL url(url_fixer::FixupURL(base::SysNSStringToUTF8([urls objectAtIndex:0]),
std::string()));
if (url.SchemeIs(url::kJavaScriptScheme)) {
browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
}
OpenURLParams params(
url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
}
// (URLDropTargetController protocol)
- (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
// TODO(viettrungluu): This code is more or less copied from the code in
// |TabStripController|. I'll refactor this soon to make it common and expand
// its capabilities (e.g., allow text DnD).
// If the input is plain text, classify the input and make the URL.
AutocompleteMatch match;
AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
base::SysNSStringToUTF16(text), false, false,
metrics::OmniboxEventProto::BLANK, &match, NULL);
GURL url(match.destination_url);
OpenURLParams params(
url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
}
// (URLDropTargetController protocol)
- (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
// Do nothing.
}
// (URLDropTargetController protocol)
- (void)hideDropURLsIndicatorInView:(NSView*)view {
// Do nothing.
}
// (URLDropTargetController protocol)
- (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
return drag_util::IsUnsupportedDropData(profile_, info);
}
@end