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/mac_util.h"
11 #include "base/memory/singleton.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
18 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
19 #include "chrome/browser/autocomplete/autocomplete_match.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/search_engines/template_url_service.h"
25 #include "chrome/browser/themes/theme_service.h"
26 #include "chrome/browser/ui/browser.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.h"
40 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button.h"
41 #import "chrome/browser/ui/cocoa/toolbar/toolbar_view.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/global_error/global_error_service.h"
46 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
47 #include "chrome/browser/ui/omnibox/omnibox_view.h"
48 #include "chrome/browser/ui/tabs/tab_strip_model.h"
49 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
50 #include "chrome/browser/upgrade_detector.h"
51 #include "chrome/common/net/url_fixer_upper.h"
52 #include "chrome/common/pref_names.h"
53 #include "content/public/browser/notification_details.h"
54 #include "content/public/browser/notification_observer.h"
55 #include "content/public/browser/notification_service.h"
56 #include "content/public/browser/web_contents.h"
57 #include "grit/chromium_strings.h"
58 #include "grit/generated_resources.h"
59 #include "grit/theme_resources.h"
60 #import "ui/base/cocoa/menu_controller.h"
61 #include "ui/base/l10n/l10n_util.h"
62 #include "ui/base/l10n/l10n_util_mac.h"
63 #include "ui/base/resource/resource_bundle.h"
64 #include "ui/gfx/image/image.h"
65 #include "ui/gfx/rect.h"
67 using content::OpenURLParams;
68 using content::Referrer;
69 using content::WebContents;
73 // Height of the toolbar in pixels when the bookmark bar is closed.
74 const CGFloat kBaseToolbarHeightNormal = 35.0;
76 // The minimum width of the location bar in pixels.
77 const CGFloat kMinimumLocationBarWidth = 100.0;
79 // The duration of any animation that occurs within the toolbar in seconds.
80 const CGFloat kAnimationDuration = 0.2;
82 // The amount of left padding that the wrench menu should have.
83 const CGFloat kWrenchMenuLeftPadding = 3.0;
87 @interface ToolbarController()
88 @property(assign, nonatomic) Browser* browser;
89 - (void)addAccessibilityDescriptions;
90 - (void)initCommandStatus:(CommandUpdater*)commands;
91 - (void)prefChanged:(const std::string&)prefName;
92 - (BackgroundGradientView*)backgroundGradientView;
93 - (void)toolbarFrameChanged;
94 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate;
95 - (void)maintainMinimumLocationBarWidth;
96 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification;
97 - (void)browserActionsContainerDragged:(NSNotification*)notification;
98 - (void)browserActionsContainerDragFinished:(NSNotification*)notification;
99 - (void)browserActionsVisibilityChanged:(NSNotification*)notification;
100 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate;
101 - (void)updateWrenchButtonSeverity;
104 namespace ToolbarControllerInternal {
106 // A class registered for C++ notifications. This is used to detect changes in
107 // preferences and upgrade available notifications. Bridges the notification
108 // back to the ToolbarController.
109 class NotificationBridge
110 : public content::NotificationObserver {
112 explicit NotificationBridge(ToolbarController* controller)
113 : controller_(controller) {
114 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
115 content::NotificationService::AllSources());
116 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
117 content::Source<Profile>([controller browser]->profile()));
120 // Overridden from content::NotificationObserver:
121 virtual void Observe(int type,
122 const content::NotificationSource& source,
123 const content::NotificationDetails& details) OVERRIDE {
125 case chrome::NOTIFICATION_UPGRADE_RECOMMENDED:
126 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
127 [controller_ updateWrenchButtonSeverity];
134 void OnPreferenceChanged(const std::string& pref_name) {
135 [controller_ prefChanged:pref_name];
139 ToolbarController* controller_; // weak, owns us
141 content::NotificationRegistrar registrar_;
144 } // namespace ToolbarControllerInternal
146 @implementation ToolbarController
148 @synthesize browser = browser_;
150 - (id)initWithCommands:(CommandUpdater*)commands
151 profile:(Profile*)profile
152 browser:(Browser*)browser
153 resizeDelegate:(id<ViewResizer>)resizeDelegate
154 nibFileNamed:(NSString*)nibName {
155 DCHECK(commands && profile && [nibName length]);
156 if ((self = [super initWithNibName:nibName
157 bundle:base::mac::FrameworkBundle()])) {
158 commands_ = commands;
161 resizeDelegate_ = resizeDelegate;
163 hasLocationBar_ = YES;
165 // Register for notifications about state changes for the toolbar buttons
166 commandObserver_.reset(new CommandObserverBridge(self, commands));
167 commandObserver_->ObserveCommand(IDC_BACK);
168 commandObserver_->ObserveCommand(IDC_FORWARD);
169 commandObserver_->ObserveCommand(IDC_RELOAD);
170 commandObserver_->ObserveCommand(IDC_HOME);
171 commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE);
176 - (id)initWithCommands:(CommandUpdater*)commands
177 profile:(Profile*)profile
178 browser:(Browser*)browser
179 resizeDelegate:(id<ViewResizer>)resizeDelegate {
180 if ((self = [self initWithCommands:commands
183 resizeDelegate:resizeDelegate
184 nibFileNamed:@"Toolbar"])) {
191 // Unset ViewIDs of toolbar elements.
192 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
193 // |browserActionsContainerView_| are handled by themselves.
194 view_id_util::UnsetID(backButton_);
195 view_id_util::UnsetID(forwardButton_);
196 view_id_util::UnsetID(homeButton_);
197 view_id_util::UnsetID(wrenchButton_);
199 // Make sure any code in the base class which assumes [self view] is
200 // the "parent" view continues to work.
202 hasLocationBar_ = YES;
204 [[NSNotificationCenter defaultCenter] removeObserver:self];
206 if (trackingArea_.get())
207 [[self view] removeTrackingArea:trackingArea_.get()];
211 // Called after the view is done loading and the outlets have been hooked up.
212 // Now we can hook up bridges that rely on UI objects such as the location
213 // bar and button state.
214 - (void)awakeFromNib {
215 [[backButton_ cell] setImageID:IDR_BACK
216 forButtonState:image_button_cell::kDefaultState];
217 [[backButton_ cell] setImageID:IDR_BACK_H
218 forButtonState:image_button_cell::kHoverState];
219 [[backButton_ cell] setImageID:IDR_BACK_P
220 forButtonState:image_button_cell::kPressedState];
221 [[backButton_ cell] setImageID:IDR_BACK_D
222 forButtonState:image_button_cell::kDisabledState];
224 [[forwardButton_ cell] setImageID:IDR_FORWARD
225 forButtonState:image_button_cell::kDefaultState];
226 [[forwardButton_ cell] setImageID:IDR_FORWARD_H
227 forButtonState:image_button_cell::kHoverState];
228 [[forwardButton_ cell] setImageID:IDR_FORWARD_P
229 forButtonState:image_button_cell::kPressedState];
230 [[forwardButton_ cell] setImageID:IDR_FORWARD_D
231 forButtonState:image_button_cell::kDisabledState];
233 [[reloadButton_ cell] setImageID:IDR_RELOAD
234 forButtonState:image_button_cell::kDefaultState];
235 [[reloadButton_ cell] setImageID:IDR_RELOAD_H
236 forButtonState:image_button_cell::kHoverState];
237 [[reloadButton_ cell] setImageID:IDR_RELOAD_P
238 forButtonState:image_button_cell::kPressedState];
240 [[homeButton_ cell] setImageID:IDR_HOME
241 forButtonState:image_button_cell::kDefaultState];
242 [[homeButton_ cell] setImageID:IDR_HOME_H
243 forButtonState:image_button_cell::kHoverState];
244 [[homeButton_ cell] setImageID:IDR_HOME_P
245 forButtonState:image_button_cell::kPressedState];
247 [[wrenchButton_ cell] setImageID:IDR_TOOLS
248 forButtonState:image_button_cell::kDefaultState];
249 [[wrenchButton_ cell] setImageID:IDR_TOOLS_H
250 forButtonState:image_button_cell::kHoverState];
251 [[wrenchButton_ cell] setImageID:IDR_TOOLS_P
252 forButtonState:image_button_cell::kPressedState];
254 [self updateWrenchButtonSeverity];
256 [wrenchButton_ setOpenMenuOnClick:YES];
258 [backButton_ setOpenMenuOnRightClick:YES];
259 [forwardButton_ setOpenMenuOnRightClick:YES];
261 [backButton_ setHandleMiddleClick:YES];
262 [forwardButton_ setHandleMiddleClick:YES];
263 [reloadButton_ setHandleMiddleClick:YES];
264 [homeButton_ setHandleMiddleClick:YES];
266 [self initCommandStatus:commands_];
268 locationBarView_.reset(new LocationBarViewMac(locationBar_, commands_,
269 profile_, browser_));
270 [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]];
271 // Register pref observers for the optional home and page/options buttons
272 // and then add them to the toolbar based on those prefs.
273 notificationBridge_.reset(
274 new ToolbarControllerInternal::NotificationBridge(self));
275 PrefService* prefs = profile_->GetPrefs();
276 showHomeButton_.Init(
277 prefs::kShowHomeButton, prefs,
279 &ToolbarControllerInternal::NotificationBridge::OnPreferenceChanged,
280 base::Unretained(notificationBridge_.get())));
281 [self showOptionalHomeButton];
282 [self installWrenchMenu];
284 // Create the controllers for the back/forward menus.
285 backMenuController_.reset([[BackForwardMenuController alloc]
286 initWithBrowser:browser_
287 modelType:BACK_FORWARD_MENU_TYPE_BACK
288 button:backButton_]);
289 forwardMenuController_.reset([[BackForwardMenuController alloc]
290 initWithBrowser:browser_
291 modelType:BACK_FORWARD_MENU_TYPE_FORWARD
292 button:forwardButton_]);
294 // For a popup window, the toolbar is really just a location bar
295 // (see override for [ToolbarController view], below). When going
296 // fullscreen, we remove the toolbar controller's view from the view
297 // hierarchy. Calling [locationBar_ removeFromSuperview] when going
298 // fullscreen causes it to get released, making us unhappy
299 // (http://crbug.com/18551). We avoid the problem by incrementing
300 // the retain count of the location bar; use of the scoped object
301 // helps us remember to release it.
302 locationBarRetainer_.reset([locationBar_ retain]);
304 [[CrTrackingArea alloc] initWithRect:NSZeroRect // Ignored
305 options:NSTrackingMouseMoved |
306 NSTrackingInVisibleRect |
307 NSTrackingMouseEnteredAndExited |
308 NSTrackingActiveAlways
311 NSView* toolbarView = [self view];
312 [toolbarView addTrackingArea:trackingArea_.get()];
314 // If the user has any Browser Actions installed, the container view for them
315 // may have to be resized depending on the width of the toolbar frame.
316 [toolbarView setPostsFrameChangedNotifications:YES];
317 [[NSNotificationCenter defaultCenter]
319 selector:@selector(toolbarFrameChanged)
320 name:NSViewFrameDidChangeNotification
323 // Set ViewIDs for toolbar elements which don't have their dedicated class.
324 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and
325 // |browserActionsContainerView_| are handled by themselves.
326 view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON);
327 view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON);
328 view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON);
329 view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU);
331 [self addAccessibilityDescriptions];
334 - (void)addAccessibilityDescriptions {
335 // Set accessibility descriptions. http://openradar.appspot.com/7496255
336 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK);
338 accessibilitySetOverrideValue:description
339 forAttribute:NSAccessibilityDescriptionAttribute];
340 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD);
341 [[forwardButton_ cell]
342 accessibilitySetOverrideValue:description
343 forAttribute:NSAccessibilityDescriptionAttribute];
344 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD);
345 [[reloadButton_ cell]
346 accessibilitySetOverrideValue:description
347 forAttribute:NSAccessibilityDescriptionAttribute];
348 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME);
350 accessibilitySetOverrideValue:description
351 forAttribute:NSAccessibilityDescriptionAttribute];
352 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION);
354 accessibilitySetOverrideValue:description
355 forAttribute:NSAccessibilityDescriptionAttribute];
356 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP);
357 [[wrenchButton_ cell]
358 accessibilitySetOverrideValue:description
359 forAttribute:NSAccessibilityDescriptionAttribute];
362 - (void)mouseExited:(NSEvent*)theEvent {
363 [[hoveredButton_ cell] setMouseInside:NO animate:YES];
364 [hoveredButton_ release];
365 hoveredButton_ = nil;
368 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent {
369 NSButton* targetView = (NSButton*)[[self view]
370 hitTest:[theEvent locationInWindow]];
372 // Only interpret the view as a hoverButton_ if it's both button and has a
373 // button cell that cares. GradientButtonCell derived cells care.
374 if (([targetView isKindOfClass:[NSButton class]]) &&
376 respondsToSelector:@selector(setMouseInside:animate:)]))
381 - (void)mouseMoved:(NSEvent*)theEvent {
382 NSButton* targetView = [self hoverButtonForEvent:theEvent];
383 if (hoveredButton_ != targetView) {
384 [[hoveredButton_ cell] setMouseInside:NO animate:YES];
385 [[targetView cell] setMouseInside:YES animate:YES];
386 [hoveredButton_ release];
387 hoveredButton_ = [targetView retain];
391 - (void)mouseEntered:(NSEvent*)event {
392 [self mouseMoved:event];
395 - (LocationBarViewMac*)locationBarBridge {
396 return locationBarView_.get();
399 - (void)focusLocationBar:(BOOL)selectAll {
400 if (locationBarView_.get())
401 locationBarView_->FocusLocation(selectAll ? true : false);
404 // Called when the state for a command changes to |enabled|. Update the
405 // corresponding UI element.
406 - (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled {
407 NSButton* button = nil;
410 button = backButton_;
413 button = forwardButton_;
416 button = homeButton_;
419 [button setEnabled:enabled];
422 // Init the enabled state of the buttons on the toolbar to match the state in
424 - (void)initCommandStatus:(CommandUpdater*)commands {
425 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO];
427 setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO];
428 [reloadButton_ setEnabled:YES];
429 [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO];
432 - (void)updateToolbarWithContents:(WebContents*)tab {
433 locationBarView_->Update(tab);
435 [locationBar_ updateMouseTracking];
437 if (browserActionsController_.get()) {
438 [browserActionsController_ update];
442 - (void)setStarredState:(BOOL)isStarred {
443 locationBarView_->SetStarred(isStarred ? true : false);
446 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
447 locationBarView_->ZoomChangedForActiveTab(
448 canShowBubble && ![wrenchMenuController_ isMenuOpen]);
451 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
452 [reloadButton_ setIsLoading:isLoading force:force];
455 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar {
456 [self view]; // Force nib loading.
458 hasToolbar_ = toolbar;
460 // If there's a toolbar, there must be a location bar.
461 DCHECK((toolbar && locBar) || !toolbar);
462 hasLocationBar_ = toolbar ? YES : locBar;
464 // Decide whether to hide/show based on whether there's a location bar.
465 [[self view] setHidden:!hasLocationBar_];
467 // Make location bar not editable when in a pop-up.
468 locationBarView_->SetEditable(toolbar);
477 // (Private) Returns the backdrop to the toolbar.
478 - (BackgroundGradientView*)backgroundGradientView {
479 // We really do mean |[super view]|; see our override of |-view|.
480 DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]);
481 return (BackgroundGradientView*)[super view];
484 - (id)customFieldEditorForObject:(id)obj {
485 if (obj == locationBar_) {
486 // Lazilly construct Field editor, Cocoa UI code always runs on the
487 // same thread, so there shoudn't be a race condition here.
488 if (autocompleteTextFieldEditor_.get() == nil) {
489 autocompleteTextFieldEditor_.reset(
490 [[AutocompleteTextFieldEditor alloc] init]);
493 // This needs to be called every time, otherwise notifications
494 // aren't sent correctly.
495 DCHECK(autocompleteTextFieldEditor_.get());
496 [autocompleteTextFieldEditor_.get() setFieldEditor:YES];
497 return autocompleteTextFieldEditor_.get();
502 // Returns an array of views in the order of the outlets above.
503 - (NSArray*)toolbarViews {
504 return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_,
505 homeButton_, wrenchButton_, locationBar_,
506 browserActionsContainerView_, nil];
509 // Moves |rect| to the right by |delta|, keeping the right side fixed by
510 // shrinking the width to compensate. Passing a negative value for |deltaX|
511 // moves to the left and increases the width.
512 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX {
513 NSRect frame = NSOffsetRect(rect, deltaX, 0);
514 frame.size.width -= deltaX;
518 // Show or hide the home button based on the pref.
519 - (void)showOptionalHomeButton {
520 // Ignore this message if only showing the URL bar.
523 BOOL hide = showHomeButton_.GetValue() ? NO : YES;
524 if (hide == [homeButton_ isHidden])
525 return; // Nothing to do, view state matches pref state.
527 // Always shift the text field by the width of the home button minus one pixel
528 // since the frame edges of each button are right on top of each other. When
529 // hiding the button, reverse the direction of the movement (to the left).
530 CGFloat moveX = [homeButton_ frame].size.width - 1.0;
532 moveX *= -1; // Reverse the direction of the move.
534 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame]
536 [homeButton_ setHidden:hide];
539 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it
540 // can be done every time the buttons are shown.
541 - (void)installWrenchMenu {
542 if (wrenchMenuController_.get())
545 wrenchMenuController_.reset(
546 [[WrenchMenuController alloc] initWithBrowser:browser_]);
547 [wrenchMenuController_ setUseWithPopUpButtonCell:YES];
548 [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]];
551 - (WrenchMenuController*)wrenchMenuController {
552 return wrenchMenuController_;
555 - (void)updateWrenchButtonSeverity {
556 WrenchToolbarButtonCell* cell =
557 base::mac::ObjCCastStrict<WrenchToolbarButtonCell>([wrenchButton_ cell]);
558 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
559 UpgradeDetector::UpgradeNotificationAnnoyanceLevel level =
560 UpgradeDetector::GetInstance()->upgrade_notification_stage();
561 [cell setSeverity:WrenchIconPainter::SeverityFromUpgradeLevel(level)
562 shouldAnimate:WrenchIconPainter::ShouldAnimateUpgradeLevel(level)];
566 GlobalError* error = GlobalErrorServiceFactory::GetForProfile(
567 browser_->profile())->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
569 [cell setSeverity:WrenchIconPainter::GlobalErrorSeverity()
574 [cell setSeverity:WrenchIconPainter::SEVERITY_NONE shouldAnimate:YES];
577 - (void)prefChanged:(const std::string&)prefName {
578 if (prefName == prefs::kShowHomeButton) {
579 [self showOptionalHomeButton];
583 - (void)createBrowserActionButtons {
584 if (!browserActionsController_.get()) {
585 browserActionsController_.reset([[BrowserActionsController alloc]
586 initWithBrowser:browser_
587 containerView:browserActionsContainerView_]);
588 [[NSNotificationCenter defaultCenter]
590 selector:@selector(browserActionsContainerDragged:)
591 name:kBrowserActionGrippyDraggingNotification
592 object:browserActionsController_];
593 [[NSNotificationCenter defaultCenter]
595 selector:@selector(browserActionsContainerDragFinished:)
596 name:kBrowserActionGrippyDragFinishedNotification
597 object:browserActionsController_];
598 [[NSNotificationCenter defaultCenter]
600 selector:@selector(browserActionsVisibilityChanged:)
601 name:kBrowserActionVisibilityChangedNotification
602 object:browserActionsController_];
603 [[NSNotificationCenter defaultCenter]
605 selector:@selector(adjustBrowserActionsContainerForNewWindow:)
606 name:NSWindowDidBecomeKeyNotification
607 object:[[self view] window]];
609 CGFloat containerWidth = [browserActionsContainerView_ isHidden] ? 0.0 :
610 NSWidth([browserActionsContainerView_ frame]);
611 if (containerWidth > 0.0)
612 [self adjustLocationSizeBy:(containerWidth * -1) animate:NO];
615 - (void)adjustBrowserActionsContainerForNewWindow:
616 (NSNotification*)notification {
617 [self toolbarFrameChanged];
618 [[NSNotificationCenter defaultCenter]
620 name:NSWindowDidBecomeKeyNotification
621 object:[[self view] window]];
624 - (void)browserActionsContainerDragged:(NSNotification*)notification {
625 CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
626 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
627 [browserActionsContainerView_ setCanDragLeft:!locationBarAtMinSize_];
628 [browserActionsContainerView_ setGrippyPinned:locationBarAtMinSize_];
629 [self adjustLocationSizeBy:
630 [browserActionsContainerView_ resizeDeltaX] animate:NO];
633 - (void)browserActionsContainerDragFinished:(NSNotification*)notification {
634 [browserActionsController_ resizeContainerAndAnimate:YES];
635 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES];
638 - (void)browserActionsVisibilityChanged:(NSNotification*)notification {
639 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
642 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate {
643 CGFloat locationBarXPos = NSMaxX([locationBar_ frame]);
644 CGFloat leftDistance;
646 if ([browserActionsContainerView_ isHidden]) {
647 CGFloat edgeXPos = [wrenchButton_ frame].origin.x;
648 leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding;
650 NSRect containerFrame = animate ?
651 [browserActionsContainerView_ animationEndFrame] :
652 [browserActionsContainerView_ frame];
654 leftDistance = containerFrame.origin.x - locationBarXPos;
656 if (leftDistance != 0.0)
657 [self adjustLocationSizeBy:leftDistance animate:animate];
660 - (void)maintainMinimumLocationBarWidth {
661 CGFloat locationBarWidth = NSWidth([locationBar_ frame]);
662 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth;
663 if (locationBarAtMinSize_) {
664 CGFloat dX = kMinimumLocationBarWidth - locationBarWidth;
665 [self adjustLocationSizeBy:dX animate:NO];
669 - (void)toolbarFrameChanged {
670 // Do nothing if the frame changes but no Browser Action Controller is
672 if (!browserActionsController_.get())
675 [self maintainMinimumLocationBarWidth];
677 if (locationBarAtMinSize_) {
678 // Once the grippy is pinned, leave it until it is explicity un-pinned.
679 [browserActionsContainerView_ setGrippyPinned:YES];
680 NSRect containerFrame = [browserActionsContainerView_ frame];
681 // Determine how much the container needs to move in case it's overlapping
682 // with the location bar.
683 CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x;
684 containerFrame = NSOffsetRect(containerFrame, dX, 0);
685 containerFrame.size.width -= dX;
686 [browserActionsContainerView_ setFrame:containerFrame];
687 } else if (!locationBarAtMinSize_ &&
688 [browserActionsContainerView_ grippyPinned]) {
689 // Expand out the container until it hits the saved size, then unpin the
691 // Add 0.1 pixel so that it doesn't hit the minimum width codepath above.
692 CGFloat dX = NSWidth([locationBar_ frame]) -
693 (kMinimumLocationBarWidth + 0.1);
694 NSRect containerFrame = [browserActionsContainerView_ frame];
695 containerFrame = NSOffsetRect(containerFrame, -dX, 0);
696 containerFrame.size.width += dX;
697 CGFloat savedContainerWidth = [browserActionsController_ savedWidth];
698 if (NSWidth(containerFrame) >= savedContainerWidth) {
699 containerFrame = NSOffsetRect(containerFrame,
700 NSWidth(containerFrame) - savedContainerWidth, 0);
701 containerFrame.size.width = savedContainerWidth;
702 [browserActionsContainerView_ setGrippyPinned:NO];
704 [browserActionsContainerView_ setFrame:containerFrame];
705 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO];
709 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate {
710 // Ensure that the location bar is in its proper place.
711 NSRect locationFrame = [locationBar_ frame];
712 locationFrame.size.width += dX;
715 [locationBar_ setFrame:locationFrame];
719 [NSAnimationContext beginGrouping];
720 [[NSAnimationContext currentContext] setDuration:kAnimationDuration];
721 [[locationBar_ animator] setFrame:locationFrame];
722 [NSAnimationContext endGrouping];
725 - (NSPoint)bookmarkBubblePoint {
726 if (locationBarView_->IsStarEnabled())
727 return locationBarView_->GetBookmarkBubblePoint();
729 // Grab bottom middle of hotdogs.
730 NSRect frame = wrenchButton_.frame;
731 NSPoint point = NSMakePoint(NSMidX(frame), NSMinY(frame));
732 // Inset to account for the whitespace around the hotdogs.
733 point.y += wrench_menu_controller::kWrenchBubblePointOffsetY;
734 return [self.view convertPoint:point toView:nil];
737 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight {
738 // With no toolbar, just ignore the compression.
740 return NSHeight([locationBar_ frame]);
742 return kBaseToolbarHeightNormal - compressByHeight;
745 - (void)setDividerOpacity:(CGFloat)opacity {
746 BackgroundGradientView* view = [self backgroundGradientView];
747 [view setShowsDivider:(opacity > 0 ? YES : NO)];
749 // We may not have a toolbar view (e.g., popup windows only have a location
751 if ([view isKindOfClass:[ToolbarView class]]) {
752 ToolbarView* toolbarView = (ToolbarView*)view;
753 [toolbarView setDividerOpacity:opacity];
756 [view setNeedsDisplay:YES];
759 - (BrowserActionsController*)browserActionsController {
760 return browserActionsController_.get();
763 - (NSView*)wrenchButton {
764 return wrenchButton_;
767 // (URLDropTargetController protocol)
768 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point {
769 // TODO(viettrungluu): This code is more or less copied from the code in
770 // |TabStripController|. I'll refactor this soon to make it common and expand
771 // its capabilities (e.g., allow text DnD).
772 if ([urls count] < 1) {
777 // TODO(viettrungluu): dropping multiple URLs?
778 if ([urls count] > 1)
781 // Get the first URL and fix it up.
782 GURL url(URLFixerUpper::FixupURL(
783 base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string()));
785 if (url.SchemeIs(content::kJavaScriptScheme)) {
786 browser_->window()->GetLocationBar()->GetOmniboxView()->SetUserText(
787 OmniboxView::StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())));
789 OpenURLParams params(
790 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false);
791 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
794 // (URLDropTargetController protocol)
795 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point {
796 // TODO(viettrungluu): This code is more or less copied from the code in
797 // |TabStripController|. I'll refactor this soon to make it common and expand
798 // its capabilities (e.g., allow text DnD).
800 // If the input is plain text, classify the input and make the URL.
801 AutocompleteMatch match;
802 AutocompleteClassifierFactory::GetForProfile(browser_->profile())->Classify(
803 base::SysNSStringToUTF16(text), false, false, &match, NULL);
804 GURL url(match.destination_url);
806 OpenURLParams params(
807 url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false);
808 browser_->tab_strip_model()->GetActiveWebContents()->OpenURL(params);
811 // (URLDropTargetController protocol)
812 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point {
816 // (URLDropTargetController protocol)
817 - (void)hideDropURLsIndicatorInView:(NSView*)view {
821 // (URLDropTargetController protocol)
822 - (BOOL)isUnsupportedDropData:(id<NSDraggingInfo>)info {
823 return drag_util::IsUnsupportedDropData(profile_, info);