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
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
9 #include "base/mac/bundle_locations.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/mac_util.h"
12 #include "base/memory/singleton.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
19 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/command_updater.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/themes/theme_service.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_commands.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #import "chrome/browser/ui/cocoa/background_gradient_view.h"
29 #include "chrome/browser/ui/cocoa/drag_util.h"
30 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
31 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h"
32 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
33 #import "chrome/browser/ui/cocoa/gradient_button_cell.h"
34 #import "chrome/browser/ui/cocoa/image_button_cell.h"
35 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
36 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
37 #import "chrome/browser/ui/cocoa/menu_button.h"
38 #import "chrome/browser/ui/cocoa/toolbar/back_forward_menu_controller.h"
39 #import "chrome/browser/ui/cocoa/toolbar/reload_button_cocoa.h"
40 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
41 #import "chrome/browser/ui/cocoa/toolbar/toolbar_view_cocoa.h"
42 #import "chrome/browser/ui/cocoa/toolbar/wrench_toolbar_button_cell.h"
43 #import "chrome/browser/ui/cocoa/view_id_util.h"
44 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h"
45 #include "chrome/browser/ui/omnibox/omnibox_view.h"
46 #include "chrome/browser/ui/tabs/tab_strip_model.h"
47 #include "chrome/browser/ui/toolbar/wrench_menu_badge_controller.h"
48 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
49 #include "chrome/common/pref_names.h"
50 #include "chrome/grit/chromium_strings.h"
51 #include "chrome/grit/generated_resources.h"
52 #include "components/metrics/proto/omnibox_event.pb.h"
53 #include "components/omnibox/autocomplete_match.h"
54 #include "components/search_engines/template_url_service.h"
55 #include "components/url_fixer/url_fixer.h"
56 #include "content/public/browser/web_contents.h"
57 #include "grit/theme_resources.h"
58 #import "ui/base/cocoa/menu_controller.h"
59 #include "ui/base/l10n/l10n_util.h"
60 #include "ui/base/l10n/l10n_util_mac.h"
61 #include "ui/gfx/geometry/rect.h"
62 #include "ui/gfx/image/image.h"
64 using content::OpenURLParams;
65 using content::Referrer;
66 using content::WebContents;
70 // Height of the toolbar in pixels when the bookmark bar is closed.
71 const CGFloat kBaseToolbarHeightNormal = 35.0;
73 // The padding above the toolbar elements. This is calculated from the values
74 // in Toolbar.xib: the height of the toolbar (35) minus the height of the child
75 // elements (29) minus the y-origin of the elements (4).
76 const CGFloat kToolbarElementTopPadding = 2.0;
78 // The minimum width of the location bar in pixels.
79 const CGFloat kMinimumLocationBarWidth = 100.0;
81 // The amount of left padding that the wrench menu should have.
82 const CGFloat kWrenchMenuLeftPadding = 3.0;
84 class BrowserActionsContainerDelegate :
85 public BrowserActionsContainerViewSizeDelegate {
87 BrowserActionsContainerDelegate(
88 AutocompleteTextField* location_bar,
89 BrowserActionsContainerView* browser_actions_container_view);
90 ~BrowserActionsContainerDelegate() override;
93 // BrowserActionsContainerSizeDelegate:
94 CGFloat GetMaxAllowedWidth() override;
96 AutocompleteTextField* location_bar_;
97 BrowserActionsContainerView* browser_actions_container_;
99 DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainerDelegate);
102 BrowserActionsContainerDelegate::BrowserActionsContainerDelegate(
103 AutocompleteTextField* location_bar,
104 BrowserActionsContainerView* browser_actions_container_view)
105 : location_bar_(location_bar),
106 browser_actions_container_(browser_actions_container_view) {
107 [browser_actions_container_ setDelegate:this];
110 BrowserActionsContainerDelegate::~BrowserActionsContainerDelegate() {
111 [browser_actions_container_ setDelegate:nil];
114 CGFloat BrowserActionsContainerDelegate::GetMaxAllowedWidth() {
115 CGFloat location_bar_flex =
116 NSWidth([location_bar_ frame]) - kMinimumLocationBarWidth;
117 return NSWidth([browser_actions_container_ frame]) + location_bar_flex;
122 @interface ToolbarController()
123 @property(assign, nonatomic) Browser* browser;
124 - (void)addAccessibilityDescriptions;
125 - (void)initCommandStatus:(CommandUpdater*)commands;
126 - (void)prefChanged:(const std::string&)prefName;
127 - (BackgroundGradientView*)backgroundGradientView;
128 - (void)toolbarFrameChanged;
129 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
130 - (void)maintainMinimumLocationBarWidth;
131 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
132 - (void)browserActionsContainerDragged:(NSNotification*)notification;
133 - (void)browserActionsVisibilityChanged:(NSNotification*)notification;
134 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification;
135 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
136 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
137 animate:(BOOL)animate;
140 namespace ToolbarControllerInternal {
142 // A class registered for C++ notifications. This is used to detect changes in
143 // preferences and upgrade available notifications. Bridges the notification
144 // back to the ToolbarController.
145 class NotificationBridge : public WrenchMenuBadgeController::Delegate {
147 explicit NotificationBridge(ToolbarController* controller)
148 : controller_(controller),
149 badge_controller_([controller browser]->profile(), this) {
151 ~NotificationBridge() override {}
153 void UpdateBadgeSeverity() {
154 badge_controller_.UpdateDelegate();
157 void UpdateBadgeSeverity(WrenchMenuBadgeController::BadgeType type,
158 WrenchIconPainter::Severity severity,
159 bool animate) override {
160 [controller_ updateWrenchButtonSeverity:severity animate:animate];
163 void OnPreferenceChanged(const std::string& pref_name) {
164 [controller_ prefChanged:pref_name];
168 ToolbarController* controller_; // weak, owns us
170 WrenchMenuBadgeController badge_controller_;
172 DISALLOW_COPY_AND_ASSIGN(NotificationBridge);
175 } // namespace ToolbarControllerInternal
177 @implementation ToolbarController
179 @synthesize browser = browser_;
181 - (id)initWithCommands:(CommandUpdater*)commands
182 profile:(Profile*)profile
183 browser:(Browser*)browser
184 resizeDelegate:(id<ViewResizer>)resizeDelegate
185 nibFileNamed:(NSString*)nibName {
186 DCHECK(commands && profile && [nibName length]);
187 if ((self = [super initWithNibName:nibName
188 bundle:base::mac::FrameworkBundle()])) {
189 commands_ = commands;
192 resizeDelegate_ = resizeDelegate;
194 hasLocationBar_ = YES;
196 // Register for notifications about state changes for the toolbar buttons
197 commandObserver_.reset(new CommandObserverBridge(self, commands));
198 commandObserver_->ObserveCommand(IDC_BACK);
199 commandObserver_->ObserveCommand(IDC_FORWARD);
200 commandObserver_->ObserveCommand(IDC_RELOAD);
201 commandObserver_->ObserveCommand(IDC_HOME);
202 commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE);
207 - (id)initWithCommands:(CommandUpdater*)commands
208 profile:(Profile*)profile
209 browser:(Browser*)browser
210 resizeDelegate:(id<ViewResizer>)resizeDelegate {
211 if ((self = [self initWithCommands:commands
214 resizeDelegate:resizeDelegate
215 nibFileNamed:@"Toolbar"])) {
222 browserActionsContainerDelegate_.reset();
224 // Unset ViewIDs of toolbar elements.
225 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
226 // |browserActionsContainerView_| are handled by themselves.
227 view_id_util::UnsetID(backButton_);
228 view_id_util::UnsetID(forwardButton_);
229 view_id_util::UnsetID(homeButton_);
230 view_id_util::UnsetID(wrenchButton_);
232 // Make sure any code in the base class which assumes [self view] is
233 // the "parent" view continues to work.
235 hasLocationBar_ = YES;
237 [[NSNotificationCenter defaultCenter] removeObserver:self];
239 if (trackingArea_.get())
240 [[self view] removeTrackingArea:trackingArea_.get()];
244 // Called after the view is done loading and the outlets have been hooked up.
245 // Now we can hook up bridges that rely on UI objects such as the location
246 // bar and button state.
247 - (void)awakeFromNib {
248 [[backButton_ cell] setImageID:IDR_BACK
249 forButtonState:image_button_cell::kDefaultState];
250 [[backButton_ cell] setImageID:IDR_BACK_H
251 forButtonState:image_button_cell::kHoverState];
252 [[backButton_ cell] setImageID:IDR_BACK_P
253 forButtonState:image_button_cell::kPressedState];
254 [[backButton_ cell] setImageID:IDR_BACK_D
255 forButtonState:image_button_cell::kDisabledState];
257 [[forwardButton_ cell] setImageID:IDR_FORWARD
258 forButtonState:image_button_cell::kDefaultState];
259 [[forwardButton_ cell] setImageID:IDR_FORWARD_H
260 forButtonState:image_button_cell::kHoverState];
261 [[forwardButton_ cell] setImageID:IDR_FORWARD_P
262 forButtonState:image_button_cell::kPressedState];
263 [[forwardButton_ cell] setImageID:IDR_FORWARD_D
264 forButtonState:image_button_cell::kDisabledState];
266 [[reloadButton_ cell] setImageID:IDR_RELOAD
267 forButtonState:image_button_cell::kDefaultState];
268 [[reloadButton_ cell] setImageID:IDR_RELOAD_H
269 forButtonState:image_button_cell::kHoverState];
270 [[reloadButton_ cell] setImageID:IDR_RELOAD_P
271 forButtonState:image_button_cell::kPressedState];
273 [[homeButton_ cell] setImageID:IDR_HOME
274 forButtonState:image_button_cell::kDefaultState];
275 [[homeButton_ cell] setImageID:IDR_HOME_H
276 forButtonState:image_button_cell::kHoverState];
277 [[homeButton_ cell] setImageID:IDR_HOME_P
278 forButtonState:image_button_cell::kPressedState];
280 [[wrenchButton_ cell] setImageID:IDR_TOOLS
281 forButtonState:image_button_cell::kDefaultState];
282 [[wrenchButton_ cell] setImageID:IDR_TOOLS_H
283 forButtonState:image_button_cell::kHoverState];
284 [[wrenchButton_ cell] setImageID:IDR_TOOLS_P
285 forButtonState:image_button_cell::kPressedState];
287 notificationBridge_.reset(
288 new ToolbarControllerInternal::NotificationBridge(self));
289 notificationBridge_->UpdateBadgeSeverity();
291 [wrenchButton_ setOpenMenuOnClick:YES];
293 [backButton_ setOpenMenuOnRightClick:YES];
294 [forwardButton_ setOpenMenuOnRightClick:YES];
296 [backButton_ setHandleMiddleClick:YES];
297 [forwardButton_ setHandleMiddleClick:YES];
298 [reloadButton_ setHandleMiddleClick:YES];
299 [homeButton_ setHandleMiddleClick:YES];
301 [self initCommandStatus:commands_];
302 [reloadButton_ setCommandUpdater:commands_];
304 locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
305 profile_, browser_));
306 [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
308 // Register pref observers for the optional home and page/options buttons
309 // and then add them to the toolbar based on those prefs.
310 PrefService* prefs = profile_->GetPrefs();
311 showHomeButton_.Init(
312 prefs::kShowHomeButton, prefs,
314 &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
315 base::Unretained(notificationBridge_.get())));
316 [self showOptionalHomeButton];
317 [self installWrenchMenu];
319 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
321 // Create the controllers for the back/forward menus.
322 backMenuController_.reset([[BackForwardMenuController alloc]
323 initWithBrowser:browser_
324 modelType:BACK_FORWARD_MENU_TYPE_BACK
325 button:backButton_]);
326 forwardMenuController_.reset([[BackForwardMenuController alloc]
327 initWithBrowser:browser_
328 modelType:BACK_FORWARD_MENU_TYPE_FORWARD
329 button:forwardButton_]);
331 // For a popup window, the toolbar is really just a location bar
332 // (see override for [ToolbarController view], below). When going
333 // fullscreen, we remove the toolbar controller's view from the view
334 // hierarchy. Calling [locationBar_ removeFromSuperview] when going
335 // fullscreen causes it to get released, making us unhappy
336 // (http://crbug.com/18551). We avoid the problem by incrementing
337 // the retain count of the location bar; use of the scoped object
338 // helps us remember to release it.
339 locationBarRetainer_.reset([locationBar_ retain]);
341 [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
342 options:NSTrackingMouseMoved |
343 NSTrackingInVisibleRect |
344 NSTrackingMouseEnteredAndExited |
345 NSTrackingActiveAlways
348 NSView* toolbarView = [self view];
349 [toolbarView addTrackingArea:trackingArea_.get()];
351 // If the user has any Browser Actions installed, the container view for them
352 // may have to be resized depending on the width of the toolbar frame.
353 [toolbarView setPostsFrameChangedNotifications:YES];
354 [[NSNotificationCenter defaultCenter]
356 selector:@selector(toolbarFrameChanged)
357 name:NSViewFrameDidChangeNotification
360 // Set ViewIDs for toolbar elements which don't have their dedicated class.
361 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
362 // |browserActionsContainerView_| are handled by themselves.
363 view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
364 view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
365 view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
366 view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
368 [self addAccessibilityDescriptions];
371 - (void)addAccessibilityDescriptions {
372 // Set accessibility descriptions. http://openradar.appspot.com/7496255
373 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
375 accessibilitySetOverrideValue:description
376 forAttribute:NSAccessibilityDescriptionAttribute];
377 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
378 [[forwardButton_ cell]
379 accessibilitySetOverrideValue:description
380 forAttribute:NSAccessibilityDescriptionAttribute];
381 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
382 [[reloadButton_ cell]
383 accessibilitySetOverrideValue:description
384 forAttribute:NSAccessibilityDescriptionAttribute];
385 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
387 accessibilitySetOverrideValue:description
388 forAttribute:NSAccessibilityDescriptionAttribute];
389 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
391 accessibilitySetOverrideValue:description
392 forAttribute:NSAccessibilityDescriptionAttribute];
393 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
394 [[wrenchButton_ cell]
395 accessibilitySetOverrideValue:description
396 forAttribute:NSAccessibilityDescriptionAttribute];
399 - (void)mouseExited:(NSEvent*)theEvent {
400 [[hoveredButton_ cell] setIsMouseInside:NO];
401 [hoveredButton_ release];
402 hoveredButton_ = nil;
405 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
406 NSButton* targetView = (NSButton*)[[self view]
407 hitTest:[theEvent locationInWindow]];
409 // Only interpret the view as a hoverButton_ if it's both button and has a
410 // button cell that cares. GradientButtonCell derived cells care.
411 if (([targetView isKindOfClass:[NSButton class]]) &&
413 respondsToSelector:@selector(setIsMouseInside:)]))
418 - (void)mouseMoved:(NSEvent*)theEvent {
419 NSButton* targetView = [self hoverButtonForEvent:theEvent];
420 if (hoveredButton_ != targetView) {
421 [[hoveredButton_ cell] setIsMouseInside:NO];
422 [[targetView cell] setIsMouseInside:YES];
423 [hoveredButton_ release];
424 hoveredButton_ = [targetView retain];
428 - (void)mouseEntered:(NSEvent*)event {
429 [self mouseMoved:event];
432 - (LocationBarViewMac*)locationBarBridge {
433 return locationBarView_.get();
436 - (void)focusLocationBar:(BOOL)selectAll {
437 if (locationBarView_.get()) {
438 locationBarView_->FocusLocation(selectAll ? true : false);
442 // Called when the state for a command changes to |enabled|. Update the
443 // corresponding UI element.
444 - (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled {
445 NSButton* button = nil;
448 button = backButton_;
451 button = forwardButton_;
454 button = homeButton_;
457 [button setEnabled:enabled];
460 // Init the enabled state of the buttons on the toolbar to match the state in
462 - (void)initCommandStatus:(CommandUpdater*)commands {
463 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
465 setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
466 [reloadButton_ setEnabled:YES];
467 [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
470 - (void)updateToolbarWithContents:(WebContents*)tab {
471 locationBarView_->Update(tab);
473 [locationBar_ updateMouseTracking];
475 if (browserActionsController_.get()) {
476 [browserActionsController_ update];
479 BOOL needReloadMenu = chrome::IsDebuggerAttachedToCurrentTab(browser_);
480 [reloadButton_ setMenuEnabled:needReloadMenu];
483 - (void)resetTabState:(WebContents*)tab {
484 locationBarView_->ResetTabState(tab);
487 - (void)setStarredState:(BOOL)isStarred {
488 locationBarView_->SetStarred(isStarred);
491 - (void)setTranslateIconLit:(BOOL)on {
492 locationBarView_->SetTranslateIconLit(on);
495 - (void)setOverflowedToolbarActionWantsToRun:(BOOL)overflowedActionWantsToRun {
496 WrenchToolbarButtonCell* cell =
497 base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
498 [cell setOverflowedToolbarActionWantsToRun:overflowedActionWantsToRun];
501 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
502 locationBarView_->ZoomChangedForActiveTab(
503 canShowBubble && ![wrenchMenuController_ isMenuOpen]);
506 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
507 [reloadButton_ setIsLoading:isLoading force:force];
510 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
511 [self view]; // Force nib loading.
513 hasToolbar_ = toolbar;
515 // If there's a toolbar, there must be a location bar.
516 DCHECK((toolbar && locBar) || !toolbar);
517 hasLocationBar_ = toolbar ? YES : locBar;
519 // Decide whether to hide/show based on whether there's a location bar.
520 [[self view] setHidden:!hasLocationBar_];
522 // Make location bar not editable when in a pop-up.
523 locationBarView_->SetEditable(toolbar);
532 // (Private) Returns the backdrop to the toolbar.
533 - (BackgroundGradientView*)backgroundGradientView {
534 // We really do mean |[super view]|; see our override of |-view|.
535 DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
536 return (BackgroundGradientView*)[super view];
539 - (id)customFieldEditorForObject:(id)obj {
540 if (obj == locationBar_) {
541 // Lazilly construct Field editor, Cocoa UI code always runs on the
542 // same thread, so there shoudn't be a race condition here.
543 if (autocompleteTextFieldEditor_.get() == nil) {
544 autocompleteTextFieldEditor_.reset(
545 [[AutocompleteTextFieldEditor alloc] init]);
548 // This needs to be called every time, otherwise notifications
549 // aren't sent correctly.
550 DCHECK(autocompleteTextFieldEditor_.get());
551 [autocompleteTextFieldEditor_.get() setFieldEditor:YES];
552 if (base::mac::IsOSSnowLeopard()) {
553 // Manually transferring the drawsBackground and backgroundColor
554 // properties is necessary to ensure anti-aliased text on 10.6.
555 [autocompleteTextFieldEditor_
556 setDrawsBackground:[locationBar_ drawsBackground]];
557 [autocompleteTextFieldEditor_
558 setBackgroundColor:[locationBar_ backgroundColor]];
560 return autocompleteTextFieldEditor_.get();
565 // Returns an array of views in the order of the outlets above.
566 - (NSArray*)toolbarViews {
567 return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
568 homeButton_, wrenchButton_, locationBar_,
569 browserActionsContainerView_, nil];
572 // Moves |rect| to the right by |delta|, keeping the right side fixed by
573 // shrinking the width to compensate. Passing a negative value for |deltaX|
574 // moves to the left and increases the width.
575 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
576 NSRect frame = NSOffsetRect(rect, deltaX, 0);
577 frame.size.width -= deltaX;
581 // Show or hide the home button based on the pref.
582 - (void)showOptionalHomeButton {
583 // Ignore this message if only showing the URL bar.
586 BOOL hide = showHomeButton_.GetValue() ? NO : YES;
587 if (hide == [homeButton_ isHidden])
588 return; // Nothing to do, view state matches pref state.
590 // Always shift the text field by the width of the home button minus one pixel
591 // since the frame edges of each button are right on top of each other. When
592 // hiding the button, reverse the direction of the movement (to the left).
593 CGFloat moveX = [homeButton_ frame].size.width - 1.0;
595 moveX *= -1; // Reverse the direction of the move.
597 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
599 [homeButton_ setHidden:hide];
602 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
603 // can be done every time the buttons are shown.
604 - (void)installWrenchMenu {
605 if (wrenchMenuController_.get())
608 wrenchMenuController_.reset(
609 [[WrenchMenuController alloc] initWithBrowser:browser_]);
610 [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
611 [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
614 - (void)updateWrenchButtonSeverity:(WrenchIconPainter::Severity)severity
615 animate:(BOOL)animate {
616 WrenchToolbarButtonCell* cell =
617 base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
618 [cell setSeverity:severity shouldAnimate:animate];
621 - (void)prefChanged:(const std::string&)prefName {
622 if (prefName == prefs::kShowHomeButton) {
623 [self showOptionalHomeButton];
627 - (void)createBrowserActionButtons {
628 if (!browserActionsController_.get()) {
629 browserActionsContainerDelegate_.reset(
630 new BrowserActionsContainerDelegate(locationBar_,
631 browserActionsContainerView_));
632 browserActionsController_.reset([[BrowserActionsController alloc]
633 initWithBrowser:browser_
634 containerView:browserActionsContainerView_
635 mainController:nil]);
636 [[NSNotificationCenter defaultCenter]
638 selector:@selector(browserActionsContainerDragged:)
639 name:kBrowserActionGrippyDraggingNotification
640 object:browserActionsContainerView_];
641 [[NSNotificationCenter defaultCenter]
643 selector:@selector(browserActionsVisibilityChanged:)
644 name:kBrowserActionVisibilityChangedNotification
645 object:browserActionsController_];
646 [[NSNotificationCenter defaultCenter]
648 selector:@selector(browserActionsContainerWillAnimate:)
649 name:kBrowserActionsContainerWillAnimate
650 object:browserActionsContainerView_];
651 [[NSNotificationCenter defaultCenter]
653 selector:@selector(adjustBrowserActionsContainerForNewWindow:)
654 name:NSWindowDidBecomeKeyNotification
655 object:[[self view] window]];
657 if (![browserActionsContainerView_ isHidden])
658 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
661 - (void)adjustBrowserActionsContainerForNewWindow:
662 (NSNotification*)notification {
663 [self toolbarFrameChanged];
664 [[NSNotificationCenter defaultCenter]
666 name:NSWindowDidBecomeKeyNotification
667 object:[[self view] window]];
670 - (void)browserActionsContainerDragged:(NSNotification*)notification {
671 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
674 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
675 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
678 - (void)browserActionsContainerWillAnimate:(NSNotification*)notification {
679 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
682 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
683 CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
684 CGFloat leftDistance = 0.0;
686 if ([browserActionsContainerView_ isHidden]) {
687 CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
688 leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
690 leftDistance = NSMinX([browserActionsContainerView_ animationEndFrame]) -
693 if (leftDistance != 0.0)
694 [self adjustLocationSizeBy:leftDistance animate:animate];
696 [locationBar_ stopAnimation];
699 - (void)maintainMinimumLocationBarWidth {
700 CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
701 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
702 if (locationBarAtMinSize_) {
703 CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
704 [self adjustLocationSizeBy:dX animate:NO];
708 - (void)toolbarFrameChanged {
709 // Do nothing if the frame changes but no Browser Action Controller is
711 if (!browserActionsController_.get())
714 if ([browserActionsContainerView_ isAnimating]) {
715 // If the browser actions container is animating, we need to stop it first,
716 // because the frame it's animating for could be incorrect with the new
717 // bounds (if, for instance, the bookmark bar was added).
718 // This will advance to the end of the animation, so we also need to adjust
720 [browserActionsContainerView_ stopAnimation];
721 NSRect containerFrame = [browserActionsContainerView_ frame];
722 containerFrame.origin.y =
723 NSHeight([[self view] frame]) - NSHeight(containerFrame) -
724 kToolbarElementTopPadding;
725 [browserActionsContainerView_ setFrame:containerFrame];
726 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
729 [self maintainMinimumLocationBarWidth];
731 if (locationBarAtMinSize_) {
732 // Once the grippy is pinned, leave it until it is explicity un-pinned.
733 [browserActionsContainerView_ setGrippyPinned:YES];
734 NSRect containerFrame = [browserActionsContainerView_ frame];
735 // Determine how much the container needs to move in case it's overlapping
736 // with the location bar.
737 CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
738 containerFrame = NSOffsetRect(containerFrame, dX, 0);
739 containerFrame.size.width -= dX;
740 [browserActionsContainerView_ setFrame:containerFrame];
741 } else if (!locationBarAtMinSize_ &&
742 [browserActionsContainerView_ grippyPinned]) {
743 // Expand out the container until it hits the saved size, then unpin the
745 // Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
746 CGFloat dX = NSWidth([locationBar_ frame]) -
747 (kMinimumLocationBarWidth + 0.1);
748 NSRect containerFrame = [browserActionsContainerView_ frame];
749 containerFrame = NSOffsetRect(containerFrame, -dX, 0);
750 containerFrame.size.width += dX;
751 CGFloat savedContainerWidth =
752 [browserActionsController_ preferredSize].width();
753 if (NSWidth(containerFrame) >= savedContainerWidth) {
754 containerFrame = NSOffsetRect(containerFrame,
755 NSWidth(containerFrame) - savedContainerWidth, 0);
756 containerFrame.size.width = savedContainerWidth;
757 [browserActionsContainerView_ setGrippyPinned:NO];
759 [browserActionsContainerView_ setFrame:containerFrame];
760 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
764 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
765 // Ensure that the location bar is in its proper place.
766 NSRect locationFrame = [locationBar_ frame];
767 locationFrame.size.width += dX;
769 [locationBar_ stopAnimation];
772 [locationBar_ animateToFrame:locationFrame];
774 [locationBar_ setFrame:locationFrame];
777 - (NSPoint)bookmarkBubblePoint {
778 if (locationBarView_->IsStarEnabled())
779 return locationBarView_->GetBookmarkBubblePoint();
781 // Grab bottom middle of hotdogs.
782 NSRect frame = wrenchButton_.frame;
783 NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
784 // Inset to account for the whitespace around the hotdogs.
785 point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
786 return [self.view convertPoint:point toView:nil];
789 - (NSPoint)managePasswordsBubblePoint {
790 return locationBarView_->GetManagePasswordsBubblePoint();
793 - (NSPoint)translateBubblePoint {
794 return locationBarView_->GetTranslateBubblePoint();
797 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
798 // With no toolbar, just ignore the compression.
800 return NSHeight([locationBar_ frame]);
802 return kBaseToolbarHeightNormal - compressByHeight;
805 - (void)setDividerOpacity:(CGFloat)opacity {
806 BackgroundGradientView* view = [self backgroundGradientView];
807 [view setShowsDivider:(opacity > 0 ? YES : NO)];
809 // We may not have a toolbar view (e.g., popup windows only have a location
811 if ([view isKindOfClass:[ToolbarView class]]) {
812 ToolbarView* toolbarView = (ToolbarView*)view;
813 [toolbarView setDividerOpacity:opacity];
816 [view setNeedsDisplay:YES];
819 - (BrowserActionsController*)browserActionsController {
820 return browserActionsController_.get();
823 - (NSView*)wrenchButton {
824 return wrenchButton_;
827 - (WrenchMenuController*)wrenchMenuController {
828 return wrenchMenuController_.get();
831 // (URLDropTargetController protocol)
832 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
833 // TODO(viettrungluu): This code is more or less copied from the code in
834 // |TabStripController|. I'll refactor this soon to make it common and expand
835 // its capabilities (e.g., allow text DnD).
836 if ([urls count] < 1) {
841 // TODO(viettrungluu): dropping multiple URLs?
842 if ([urls count] > 1)
845 // Get the first URL and fix it up.
846 GURL url(url_fixer::FixupURL(base::SysNSStringToUTF8([urls objectAtIndex:0]),
849 if (url.SchemeIs(url::kJavaScriptScheme)) {
850 browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
851 OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
853 OpenURLParams params(
854 url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
855 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
858 // (URLDropTargetController protocol)
859 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
860 // TODO(viettrungluu): This code is more or less copied from the code in
861 // |TabStripController|. I'll refactor this soon to make it common and expand
862 // its capabilities (e.g., allow text DnD).
864 // If the input is plain text, classify the input and make the URL.
865 AutocompleteMatch match;
866 AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
867 base::SysNSStringToUTF16(text), false, false,
868 metrics::OmniboxEventProto::BLANK, &match, NULL);
869 GURL url(match.destination_url);
871 OpenURLParams params(
872 url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
873 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
876 // (URLDropTargetController protocol)
877 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
881 // (URLDropTargetController protocol)
882 - (void)hideDropURLsIndicatorInView:(NSView*)view {
886 // (URLDropTargetController protocol)
887 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
888 return drag_util::IsUnsupportedDropData(profile_, info);