| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/toolbar_controller.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "app/l10n_util_mac.h" | |
| 11 #include "app/mac/nsimage_cache.h" | |
| 12 #include "app/resource_bundle.h" | |
| 13 #include "base/mac/mac_util.h" | |
| 14 #include "base/singleton.h" | |
| 15 #include "base/sys_string_conversions.h" | |
| 16 #include "chrome/app/chrome_command_ids.h" | |
| 17 #include "chrome/browser/autocomplete/autocomplete.h" | |
| 18 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | |
| 19 #include "chrome/browser/autocomplete/autocomplete_edit_view.h" | |
| 20 #include "chrome/browser/autocomplete/autocomplete_match.h" | |
| 21 #include "chrome/browser/background_page_tracker.h" | |
| 22 #include "chrome/browser/net/url_fixer_upper.h" | |
| 23 #include "chrome/browser/prefs/pref_service.h" | |
| 24 #include "chrome/browser/profiles/profile.h" | |
| 25 #include "chrome/browser/search_engines/template_url_model.h" | |
| 26 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 27 #include "chrome/browser/themes/browser_theme_provider.h" | |
| 28 #include "chrome/browser/upgrade_detector.h" | |
| 29 #include "chrome/browser/ui/browser.h" | |
| 30 #include "chrome/browser/ui/browser_window.h" | |
| 31 #import "chrome/browser/ui/cocoa/accelerators_cocoa.h" | |
| 32 #import "chrome/browser/ui/cocoa/back_forward_menu_controller.h" | |
| 33 #import "chrome/browser/ui/cocoa/background_gradient_view.h" | |
| 34 #import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h" | |
| 35 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h" | |
| 36 #import "chrome/browser/ui/cocoa/extensions/browser_actions_container_view.h" | |
| 37 #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h" | |
| 38 #import "chrome/browser/ui/cocoa/gradient_button_cell.h" | |
| 39 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h" | |
| 40 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" | |
| 41 #import "chrome/browser/ui/cocoa/menu_button.h" | |
| 42 #import "chrome/browser/ui/cocoa/menu_controller.h" | |
| 43 #import "chrome/browser/ui/cocoa/reload_button.h" | |
| 44 #import "chrome/browser/ui/cocoa/toolbar_view.h" | |
| 45 #import "chrome/browser/ui/cocoa/view_id_util.h" | |
| 46 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h" | |
| 47 #include "chrome/browser/ui/toolbar/toolbar_model.h" | |
| 48 #include "chrome/browser/ui/toolbar/wrench_menu_model.h" | |
| 49 #include "chrome/common/notification_details.h" | |
| 50 #include "chrome/common/notification_observer.h" | |
| 51 #include "chrome/common/notification_service.h" | |
| 52 #include "chrome/common/notification_type.h" | |
| 53 #include "chrome/common/pref_names.h" | |
| 54 #include "gfx/rect.h" | |
| 55 #include "grit/chromium_strings.h" | |
| 56 #include "grit/generated_resources.h" | |
| 57 #include "grit/theme_resources.h" | |
| 58 #include "ui/base/models/accelerator_cocoa.h" | |
| 59 #include "ui/base/models/menu_model.h" | |
| 60 | |
| 61 namespace { | |
| 62 | |
| 63 // Names of images in the bundle for buttons. | |
| 64 NSString* const kBackButtonImageName = @"back_Template.pdf"; | |
| 65 NSString* const kForwardButtonImageName = @"forward_Template.pdf"; | |
| 66 NSString* const kReloadButtonReloadImageName = @"reload_Template.pdf"; | |
| 67 NSString* const kReloadButtonStopImageName = @"stop_Template.pdf"; | |
| 68 NSString* const kHomeButtonImageName = @"home_Template.pdf"; | |
| 69 NSString* const kWrenchButtonImageName = @"tools_Template.pdf"; | |
| 70 | |
| 71 // Height of the toolbar in pixels when the bookmark bar is closed. | |
| 72 const CGFloat kBaseToolbarHeight = 35.0; | |
| 73 | |
| 74 // The minimum width of the location bar in pixels. | |
| 75 const CGFloat kMinimumLocationBarWidth = 100.0; | |
| 76 | |
| 77 // The duration of any animation that occurs within the toolbar in seconds. | |
| 78 const CGFloat kAnimationDuration = 0.2; | |
| 79 | |
| 80 // The amount of left padding that the wrench menu should have. | |
| 81 const CGFloat kWrenchMenuLeftPadding = 3.0; | |
| 82 | |
| 83 } // namespace | |
| 84 | |
| 85 @interface ToolbarController(Private) | |
| 86 - (void)addAccessibilityDescriptions; | |
| 87 - (void)initCommandStatus:(CommandUpdater*)commands; | |
| 88 - (void)prefChanged:(std::string*)prefName; | |
| 89 - (BackgroundGradientView*)backgroundGradientView; | |
| 90 - (void)toolbarFrameChanged; | |
| 91 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate; | |
| 92 - (void)maintainMinimumLocationBarWidth; | |
| 93 - (void)adjustBrowserActionsContainerForNewWindow:(NSNotification*)notification; | |
| 94 - (void)browserActionsContainerDragged:(NSNotification*)notification; | |
| 95 - (void)browserActionsContainerDragFinished:(NSNotification*)notification; | |
| 96 - (void)browserActionsVisibilityChanged:(NSNotification*)notification; | |
| 97 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate; | |
| 98 - (void)badgeWrenchMenuIfNeeded; | |
| 99 @end | |
| 100 | |
| 101 namespace ToolbarControllerInternal { | |
| 102 | |
| 103 // A C++ delegate that handles the accelerators in the wrench menu. | |
| 104 class WrenchAcceleratorDelegate : public ui::AcceleratorProvider { | |
| 105 public: | |
| 106 virtual bool GetAcceleratorForCommandId(int command_id, | |
| 107 ui::Accelerator* accelerator_generic) { | |
| 108 // Downcast so that when the copy constructor is invoked below, the key | |
| 109 // string gets copied, too. | |
| 110 ui::AcceleratorCocoa* out_accelerator = | |
| 111 static_cast<ui::AcceleratorCocoa*>(accelerator_generic); | |
| 112 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance(); | |
| 113 const ui::AcceleratorCocoa* accelerator = | |
| 114 keymap->GetAcceleratorForCommand(command_id); | |
| 115 if (accelerator) { | |
| 116 *out_accelerator = *accelerator; | |
| 117 return true; | |
| 118 } | |
| 119 return false; | |
| 120 } | |
| 121 }; | |
| 122 | |
| 123 // A class registered for C++ notifications. This is used to detect changes in | |
| 124 // preferences and upgrade available notifications. Bridges the notification | |
| 125 // back to the ToolbarController. | |
| 126 class NotificationBridge : public NotificationObserver { | |
| 127 public: | |
| 128 explicit NotificationBridge(ToolbarController* controller) | |
| 129 : controller_(controller) { | |
| 130 registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED, | |
| 131 NotificationService::AllSources()); | |
| 132 registrar_.Add(this, | |
| 133 NotificationType::BACKGROUND_PAGE_TRACKER_CHANGED, | |
| 134 NotificationService::AllSources()); | |
| 135 } | |
| 136 | |
| 137 // Overridden from NotificationObserver: | |
| 138 virtual void Observe(NotificationType type, | |
| 139 const NotificationSource& source, | |
| 140 const NotificationDetails& details) { | |
| 141 switch (type.value) { | |
| 142 case NotificationType::PREF_CHANGED: | |
| 143 [controller_ prefChanged:Details<std::string>(details).ptr()]; | |
| 144 break; | |
| 145 case NotificationType::BACKGROUND_PAGE_TRACKER_CHANGED: | |
| 146 // fall-through | |
| 147 case NotificationType::UPGRADE_RECOMMENDED: | |
| 148 [controller_ badgeWrenchMenuIfNeeded]; | |
| 149 break; | |
| 150 default: | |
| 151 NOTREACHED(); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 private: | |
| 156 ToolbarController* controller_; // weak, owns us | |
| 157 | |
| 158 NotificationRegistrar registrar_; | |
| 159 }; | |
| 160 | |
| 161 } // namespace ToolbarControllerInternal | |
| 162 | |
| 163 @implementation ToolbarController | |
| 164 | |
| 165 - (id)initWithModel:(ToolbarModel*)model | |
| 166 commands:(CommandUpdater*)commands | |
| 167 profile:(Profile*)profile | |
| 168 browser:(Browser*)browser | |
| 169 resizeDelegate:(id<ViewResizer>)resizeDelegate | |
| 170 nibFileNamed:(NSString*)nibName { | |
| 171 DCHECK(model && commands && profile && [nibName length]); | |
| 172 if ((self = [super initWithNibName:nibName | |
| 173 bundle:base::mac::MainAppBundle()])) { | |
| 174 toolbarModel_ = model; | |
| 175 commands_ = commands; | |
| 176 profile_ = profile; | |
| 177 browser_ = browser; | |
| 178 resizeDelegate_ = resizeDelegate; | |
| 179 hasToolbar_ = YES; | |
| 180 hasLocationBar_ = YES; | |
| 181 | |
| 182 // Register for notifications about state changes for the toolbar buttons | |
| 183 commandObserver_.reset(new CommandObserverBridge(self, commands)); | |
| 184 commandObserver_->ObserveCommand(IDC_BACK); | |
| 185 commandObserver_->ObserveCommand(IDC_FORWARD); | |
| 186 commandObserver_->ObserveCommand(IDC_RELOAD); | |
| 187 commandObserver_->ObserveCommand(IDC_HOME); | |
| 188 commandObserver_->ObserveCommand(IDC_BOOKMARK_PAGE); | |
| 189 } | |
| 190 return self; | |
| 191 } | |
| 192 | |
| 193 - (id)initWithModel:(ToolbarModel*)model | |
| 194 commands:(CommandUpdater*)commands | |
| 195 profile:(Profile*)profile | |
| 196 browser:(Browser*)browser | |
| 197 resizeDelegate:(id<ViewResizer>)resizeDelegate { | |
| 198 if ((self = [self initWithModel:model | |
| 199 commands:commands | |
| 200 profile:profile | |
| 201 browser:browser | |
| 202 resizeDelegate:resizeDelegate | |
| 203 nibFileNamed:@"Toolbar"])) { | |
| 204 } | |
| 205 return self; | |
| 206 } | |
| 207 | |
| 208 | |
| 209 - (void)dealloc { | |
| 210 // Unset ViewIDs of toolbar elements. | |
| 211 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and | |
| 212 // |browserActionsContainerView_| are handled by themselves. | |
| 213 view_id_util::UnsetID(backButton_); | |
| 214 view_id_util::UnsetID(forwardButton_); | |
| 215 view_id_util::UnsetID(homeButton_); | |
| 216 view_id_util::UnsetID(wrenchButton_); | |
| 217 | |
| 218 // Make sure any code in the base class which assumes [self view] is | |
| 219 // the "parent" view continues to work. | |
| 220 hasToolbar_ = YES; | |
| 221 hasLocationBar_ = YES; | |
| 222 | |
| 223 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 224 | |
| 225 if (trackingArea_.get()) | |
| 226 [[self view] removeTrackingArea:trackingArea_.get()]; | |
| 227 [super dealloc]; | |
| 228 } | |
| 229 | |
| 230 // Called after the view is done loading and the outlets have been hooked up. | |
| 231 // Now we can hook up bridges that rely on UI objects such as the location | |
| 232 // bar and button state. | |
| 233 - (void)awakeFromNib { | |
| 234 // A bug in AppKit (<rdar://7298597>, <http://openradar.me/7298597>) causes | |
| 235 // images loaded directly from nibs in a framework to not get their "template" | |
| 236 // flags set properly. Thus, despite the images being set on the buttons in | |
| 237 // the xib, we must set them in code. | |
| 238 [backButton_ setImage:app::mac::GetCachedImageWithName(kBackButtonImageName)]; | |
| 239 [forwardButton_ setImage: | |
| 240 app::mac::GetCachedImageWithName(kForwardButtonImageName)]; | |
| 241 [reloadButton_ setImage: | |
| 242 app::mac::GetCachedImageWithName(kReloadButtonReloadImageName)]; | |
| 243 [homeButton_ setImage: | |
| 244 app::mac::GetCachedImageWithName(kHomeButtonImageName)]; | |
| 245 [wrenchButton_ setImage: | |
| 246 app::mac::GetCachedImageWithName(kWrenchButtonImageName)]; | |
| 247 [self badgeWrenchMenuIfNeeded]; | |
| 248 | |
| 249 [wrenchButton_ setOpenMenuOnClick:YES]; | |
| 250 | |
| 251 [backButton_ setShowsBorderOnlyWhileMouseInside:YES]; | |
| 252 [forwardButton_ setShowsBorderOnlyWhileMouseInside:YES]; | |
| 253 [reloadButton_ setShowsBorderOnlyWhileMouseInside:YES]; | |
| 254 [homeButton_ setShowsBorderOnlyWhileMouseInside:YES]; | |
| 255 [wrenchButton_ setShowsBorderOnlyWhileMouseInside:YES]; | |
| 256 | |
| 257 [self initCommandStatus:commands_]; | |
| 258 locationBarView_.reset(new LocationBarViewMac(locationBar_, | |
| 259 commands_, toolbarModel_, | |
| 260 profile_, browser_)); | |
| 261 [locationBar_ setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; | |
| 262 // Register pref observers for the optional home and page/options buttons | |
| 263 // and then add them to the toolbar based on those prefs. | |
| 264 notificationBridge_.reset( | |
| 265 new ToolbarControllerInternal::NotificationBridge(self)); | |
| 266 PrefService* prefs = profile_->GetPrefs(); | |
| 267 showHomeButton_.Init(prefs::kShowHomeButton, prefs, | |
| 268 notificationBridge_.get()); | |
| 269 showPageOptionButtons_.Init(prefs::kShowPageOptionsButtons, prefs, | |
| 270 notificationBridge_.get()); | |
| 271 [self showOptionalHomeButton]; | |
| 272 [self installWrenchMenu]; | |
| 273 | |
| 274 // Create the controllers for the back/forward menus. | |
| 275 backMenuController_.reset([[BackForwardMenuController alloc] | |
| 276 initWithBrowser:browser_ | |
| 277 modelType:BACK_FORWARD_MENU_TYPE_BACK | |
| 278 button:backButton_]); | |
| 279 forwardMenuController_.reset([[BackForwardMenuController alloc] | |
| 280 initWithBrowser:browser_ | |
| 281 modelType:BACK_FORWARD_MENU_TYPE_FORWARD | |
| 282 button:forwardButton_]); | |
| 283 | |
| 284 // For a popup window, the toolbar is really just a location bar | |
| 285 // (see override for [ToolbarController view], below). When going | |
| 286 // fullscreen, we remove the toolbar controller's view from the view | |
| 287 // hierarchy. Calling [locationBar_ removeFromSuperview] when going | |
| 288 // fullscreen causes it to get released, making us unhappy | |
| 289 // (http://crbug.com/18551). We avoid the problem by incrementing | |
| 290 // the retain count of the location bar; use of the scoped object | |
| 291 // helps us remember to release it. | |
| 292 locationBarRetainer_.reset([locationBar_ retain]); | |
| 293 trackingArea_.reset( | |
| 294 [[NSTrackingArea alloc] initWithRect:NSZeroRect // Ignored | |
| 295 options:NSTrackingMouseMoved | | |
| 296 NSTrackingInVisibleRect | | |
| 297 NSTrackingMouseEnteredAndExited | | |
| 298 NSTrackingActiveAlways | |
| 299 owner:self | |
| 300 userInfo:nil]); | |
| 301 NSView* toolbarView = [self view]; | |
| 302 [toolbarView addTrackingArea:trackingArea_.get()]; | |
| 303 | |
| 304 // If the user has any Browser Actions installed, the container view for them | |
| 305 // may have to be resized depending on the width of the toolbar frame. | |
| 306 [toolbarView setPostsFrameChangedNotifications:YES]; | |
| 307 [[NSNotificationCenter defaultCenter] | |
| 308 addObserver:self | |
| 309 selector:@selector(toolbarFrameChanged) | |
| 310 name:NSViewFrameDidChangeNotification | |
| 311 object:toolbarView]; | |
| 312 | |
| 313 // Set ViewIDs for toolbar elements which don't have their dedicated class. | |
| 314 // ViewIDs of |toolbarView|, |reloadButton_|, |locationBar_| and | |
| 315 // |browserActionsContainerView_| are handled by themselves. | |
| 316 view_id_util::SetID(backButton_, VIEW_ID_BACK_BUTTON); | |
| 317 view_id_util::SetID(forwardButton_, VIEW_ID_FORWARD_BUTTON); | |
| 318 view_id_util::SetID(homeButton_, VIEW_ID_HOME_BUTTON); | |
| 319 view_id_util::SetID(wrenchButton_, VIEW_ID_APP_MENU); | |
| 320 | |
| 321 [self addAccessibilityDescriptions]; | |
| 322 } | |
| 323 | |
| 324 - (void)addAccessibilityDescriptions { | |
| 325 // Set accessibility descriptions. http://openradar.appspot.com/7496255 | |
| 326 NSString* description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_BACK); | |
| 327 [[backButton_ cell] | |
| 328 accessibilitySetOverrideValue:description | |
| 329 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 330 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_FORWARD); | |
| 331 [[forwardButton_ cell] | |
| 332 accessibilitySetOverrideValue:description | |
| 333 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 334 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_RELOAD); | |
| 335 [[reloadButton_ cell] | |
| 336 accessibilitySetOverrideValue:description | |
| 337 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 338 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_HOME); | |
| 339 [[homeButton_ cell] | |
| 340 accessibilitySetOverrideValue:description | |
| 341 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 342 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_LOCATION); | |
| 343 [[locationBar_ cell] | |
| 344 accessibilitySetOverrideValue:description | |
| 345 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 346 description = l10n_util::GetNSStringWithFixup(IDS_ACCNAME_APP); | |
| 347 [[wrenchButton_ cell] | |
| 348 accessibilitySetOverrideValue:description | |
| 349 forAttribute:NSAccessibilityDescriptionAttribute]; | |
| 350 } | |
| 351 | |
| 352 - (void)mouseExited:(NSEvent*)theEvent { | |
| 353 [[hoveredButton_ cell] setMouseInside:NO animate:YES]; | |
| 354 [hoveredButton_ release]; | |
| 355 hoveredButton_ = nil; | |
| 356 } | |
| 357 | |
| 358 - (NSButton*)hoverButtonForEvent:(NSEvent*)theEvent { | |
| 359 NSButton* targetView = (NSButton*)[[self view] | |
| 360 hitTest:[theEvent locationInWindow]]; | |
| 361 | |
| 362 // Only interpret the view as a hoverButton_ if it's both button and has a | |
| 363 // button cell that cares. GradientButtonCell derived cells care. | |
| 364 if (([targetView isKindOfClass:[NSButton class]]) && | |
| 365 ([[targetView cell] | |
| 366 respondsToSelector:@selector(setMouseInside:animate:)])) | |
| 367 return targetView; | |
| 368 return nil; | |
| 369 } | |
| 370 | |
| 371 - (void)mouseMoved:(NSEvent*)theEvent { | |
| 372 NSButton* targetView = [self hoverButtonForEvent:theEvent]; | |
| 373 if (hoveredButton_ != targetView) { | |
| 374 [[hoveredButton_ cell] setMouseInside:NO animate:YES]; | |
| 375 [[targetView cell] setMouseInside:YES animate:YES]; | |
| 376 [hoveredButton_ release]; | |
| 377 hoveredButton_ = [targetView retain]; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 - (void)mouseEntered:(NSEvent*)event { | |
| 382 [self mouseMoved:event]; | |
| 383 } | |
| 384 | |
| 385 - (LocationBarViewMac*)locationBarBridge { | |
| 386 return locationBarView_.get(); | |
| 387 } | |
| 388 | |
| 389 - (void)focusLocationBar:(BOOL)selectAll { | |
| 390 if (locationBarView_.get()) | |
| 391 locationBarView_->FocusLocation(selectAll ? true : false); | |
| 392 } | |
| 393 | |
| 394 // Called when the state for a command changes to |enabled|. Update the | |
| 395 // corresponding UI element. | |
| 396 - (void)enabledStateChangedForCommand:(NSInteger)command enabled:(BOOL)enabled { | |
| 397 NSButton* button = nil; | |
| 398 switch (command) { | |
| 399 case IDC_BACK: | |
| 400 button = backButton_; | |
| 401 break; | |
| 402 case IDC_FORWARD: | |
| 403 button = forwardButton_; | |
| 404 break; | |
| 405 case IDC_HOME: | |
| 406 button = homeButton_; | |
| 407 break; | |
| 408 } | |
| 409 [button setEnabled:enabled]; | |
| 410 } | |
| 411 | |
| 412 // Init the enabled state of the buttons on the toolbar to match the state in | |
| 413 // the controller. | |
| 414 - (void)initCommandStatus:(CommandUpdater*)commands { | |
| 415 [backButton_ setEnabled:commands->IsCommandEnabled(IDC_BACK) ? YES : NO]; | |
| 416 [forwardButton_ | |
| 417 setEnabled:commands->IsCommandEnabled(IDC_FORWARD) ? YES : NO]; | |
| 418 [reloadButton_ setEnabled:YES]; | |
| 419 [homeButton_ setEnabled:commands->IsCommandEnabled(IDC_HOME) ? YES : NO]; | |
| 420 } | |
| 421 | |
| 422 - (void)updateToolbarWithContents:(TabContents*)tab | |
| 423 shouldRestoreState:(BOOL)shouldRestore { | |
| 424 locationBarView_->Update(tab, shouldRestore ? true : false); | |
| 425 | |
| 426 [locationBar_ updateCursorAndToolTipRects]; | |
| 427 | |
| 428 if (browserActionsController_.get()) { | |
| 429 [browserActionsController_ update]; | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 - (void)setStarredState:(BOOL)isStarred { | |
| 434 locationBarView_->SetStarred(isStarred ? true : false); | |
| 435 } | |
| 436 | |
| 437 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { | |
| 438 [reloadButton_ setIsLoading:isLoading force:force]; | |
| 439 } | |
| 440 | |
| 441 - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar { | |
| 442 [self view]; // Force nib loading. | |
| 443 | |
| 444 hasToolbar_ = toolbar; | |
| 445 | |
| 446 // If there's a toolbar, there must be a location bar. | |
| 447 DCHECK((toolbar && locBar) || !toolbar); | |
| 448 hasLocationBar_ = toolbar ? YES : locBar; | |
| 449 | |
| 450 // Decide whether to hide/show based on whether there's a location bar. | |
| 451 [[self view] setHidden:!hasLocationBar_]; | |
| 452 | |
| 453 // Make location bar not editable when in a pop-up. | |
| 454 locationBarView_->SetEditable(toolbar); | |
| 455 } | |
| 456 | |
| 457 - (NSView*)view { | |
| 458 if (hasToolbar_) | |
| 459 return [super view]; | |
| 460 return locationBar_; | |
| 461 } | |
| 462 | |
| 463 // (Private) Returns the backdrop to the toolbar. | |
| 464 - (BackgroundGradientView*)backgroundGradientView { | |
| 465 // We really do mean |[super view]|; see our override of |-view|. | |
| 466 DCHECK([[super view] isKindOfClass:[BackgroundGradientView class]]); | |
| 467 return (BackgroundGradientView*)[super view]; | |
| 468 } | |
| 469 | |
| 470 - (id)customFieldEditorForObject:(id)obj { | |
| 471 if (obj == locationBar_) { | |
| 472 // Lazilly construct Field editor, Cocoa UI code always runs on the | |
| 473 // same thread, so there shoudn't be a race condition here. | |
| 474 if (autocompleteTextFieldEditor_.get() == nil) { | |
| 475 autocompleteTextFieldEditor_.reset( | |
| 476 [[AutocompleteTextFieldEditor alloc] init]); | |
| 477 } | |
| 478 | |
| 479 // This needs to be called every time, otherwise notifications | |
| 480 // aren't sent correctly. | |
| 481 DCHECK(autocompleteTextFieldEditor_.get()); | |
| 482 [autocompleteTextFieldEditor_.get() setFieldEditor:YES]; | |
| 483 return autocompleteTextFieldEditor_.get(); | |
| 484 } | |
| 485 return nil; | |
| 486 } | |
| 487 | |
| 488 // Returns an array of views in the order of the outlets above. | |
| 489 - (NSArray*)toolbarViews { | |
| 490 return [NSArray arrayWithObjects:backButton_, forwardButton_, reloadButton_, | |
| 491 homeButton_, wrenchButton_, locationBar_, | |
| 492 browserActionsContainerView_, nil]; | |
| 493 } | |
| 494 | |
| 495 // Moves |rect| to the right by |delta|, keeping the right side fixed by | |
| 496 // shrinking the width to compensate. Passing a negative value for |deltaX| | |
| 497 // moves to the left and increases the width. | |
| 498 - (NSRect)adjustRect:(NSRect)rect byAmount:(CGFloat)deltaX { | |
| 499 NSRect frame = NSOffsetRect(rect, deltaX, 0); | |
| 500 frame.size.width -= deltaX; | |
| 501 return frame; | |
| 502 } | |
| 503 | |
| 504 // Show or hide the home button based on the pref. | |
| 505 - (void)showOptionalHomeButton { | |
| 506 // Ignore this message if only showing the URL bar. | |
| 507 if (!hasToolbar_) | |
| 508 return; | |
| 509 BOOL hide = showHomeButton_.GetValue() ? NO : YES; | |
| 510 if (hide == [homeButton_ isHidden]) | |
| 511 return; // Nothing to do, view state matches pref state. | |
| 512 | |
| 513 // Always shift the text field by the width of the home button minus one pixel | |
| 514 // since the frame edges of each button are right on top of each other. When | |
| 515 // hiding the button, reverse the direction of the movement (to the left). | |
| 516 CGFloat moveX = [homeButton_ frame].size.width - 1.0; | |
| 517 if (hide) | |
| 518 moveX *= -1; // Reverse the direction of the move. | |
| 519 | |
| 520 [locationBar_ setFrame:[self adjustRect:[locationBar_ frame] | |
| 521 byAmount:moveX]]; | |
| 522 [homeButton_ setHidden:hide]; | |
| 523 } | |
| 524 | |
| 525 // Install the menu wrench buttons. Calling this repeatedly is inexpensive so it | |
| 526 // can be done every time the buttons are shown. | |
| 527 - (void)installWrenchMenu { | |
| 528 if (wrenchMenuModel_.get()) | |
| 529 return; | |
| 530 acceleratorDelegate_.reset( | |
| 531 new ToolbarControllerInternal::WrenchAcceleratorDelegate()); | |
| 532 | |
| 533 wrenchMenuModel_.reset(new WrenchMenuModel( | |
| 534 acceleratorDelegate_.get(), browser_)); | |
| 535 [wrenchMenuController_ setModel:wrenchMenuModel_.get()]; | |
| 536 [wrenchMenuController_ setUseWithPopUpButtonCell:YES]; | |
| 537 [wrenchButton_ setAttachedMenu:[wrenchMenuController_ menu]]; | |
| 538 } | |
| 539 | |
| 540 - (WrenchMenuController*)wrenchMenuController { | |
| 541 return wrenchMenuController_; | |
| 542 } | |
| 543 | |
| 544 - (void)badgeWrenchMenuIfNeeded { | |
| 545 | |
| 546 int badgeResource = 0; | |
| 547 if (UpgradeDetector::GetInstance()->notify_upgrade()) { | |
| 548 badgeResource = IDR_UPDATE_BADGE; | |
| 549 } else if (BackgroundPageTracker::GetInstance()-> | |
| 550 GetUnacknowledgedBackgroundPageCount() > 0) { | |
| 551 badgeResource = IDR_BACKGROUND_BADGE; | |
| 552 } else { | |
| 553 // No badge - clear the badge if one is already set. | |
| 554 if ([[wrenchButton_ cell] overlayImage]) | |
| 555 [[wrenchButton_ cell] setOverlayImage:nil]; | |
| 556 return; | |
| 557 } | |
| 558 | |
| 559 NSImage* badge = | |
| 560 ResourceBundle::GetSharedInstance().GetNativeImageNamed(badgeResource); | |
| 561 NSImage* wrenchImage = | |
| 562 app::mac::GetCachedImageWithName(kWrenchButtonImageName); | |
| 563 NSSize wrenchImageSize = [wrenchImage size]; | |
| 564 NSSize badgeSize = [badge size]; | |
| 565 | |
| 566 scoped_nsobject<NSImage> overlayImage( | |
| 567 [[NSImage alloc] initWithSize:wrenchImageSize]); | |
| 568 | |
| 569 // Draw badge in the upper right corner of the button. | |
| 570 NSPoint overlayPosition = | |
| 571 NSMakePoint(wrenchImageSize.width - badgeSize.width, | |
| 572 wrenchImageSize.height - badgeSize.height); | |
| 573 | |
| 574 [overlayImage lockFocus]; | |
| 575 [badge drawAtPoint:overlayPosition | |
| 576 fromRect:NSZeroRect | |
| 577 operation:NSCompositeSourceOver | |
| 578 fraction:1.0]; | |
| 579 [overlayImage unlockFocus]; | |
| 580 | |
| 581 [[wrenchButton_ cell] setOverlayImage:overlayImage]; | |
| 582 } | |
| 583 | |
| 584 - (void)prefChanged:(std::string*)prefName { | |
| 585 if (!prefName) return; | |
| 586 if (*prefName == prefs::kShowHomeButton) { | |
| 587 [self showOptionalHomeButton]; | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 - (void)createBrowserActionButtons { | |
| 592 if (!browserActionsController_.get()) { | |
| 593 browserActionsController_.reset([[BrowserActionsController alloc] | |
| 594 initWithBrowser:browser_ | |
| 595 containerView:browserActionsContainerView_]); | |
| 596 [[NSNotificationCenter defaultCenter] | |
| 597 addObserver:self | |
| 598 selector:@selector(browserActionsContainerDragged:) | |
| 599 name:kBrowserActionGrippyDraggingNotification | |
| 600 object:browserActionsController_]; | |
| 601 [[NSNotificationCenter defaultCenter] | |
| 602 addObserver:self | |
| 603 selector:@selector(browserActionsContainerDragFinished:) | |
| 604 name:kBrowserActionGrippyDragFinishedNotification | |
| 605 object:browserActionsController_]; | |
| 606 [[NSNotificationCenter defaultCenter] | |
| 607 addObserver:self | |
| 608 selector:@selector(browserActionsVisibilityChanged:) | |
| 609 name:kBrowserActionVisibilityChangedNotification | |
| 610 object:browserActionsController_]; | |
| 611 [[NSNotificationCenter defaultCenter] | |
| 612 addObserver:self | |
| 613 selector:@selector(adjustBrowserActionsContainerForNewWindow:) | |
| 614 name:NSWindowDidBecomeKeyNotification | |
| 615 object:[[self view] window]]; | |
| 616 } | |
| 617 CGFloat containerWidth = [browserActionsContainerView_ isHidden] ? 0.0 : | |
| 618 NSWidth([browserActionsContainerView_ frame]); | |
| 619 if (containerWidth > 0.0) | |
| 620 [self adjustLocationSizeBy:(containerWidth * -1) animate:NO]; | |
| 621 } | |
| 622 | |
| 623 - (void)adjustBrowserActionsContainerForNewWindow: | |
| 624 (NSNotification*)notification { | |
| 625 [self toolbarFrameChanged]; | |
| 626 [[NSNotificationCenter defaultCenter] | |
| 627 removeObserver:self | |
| 628 name:NSWindowDidBecomeKeyNotification | |
| 629 object:[[self view] window]]; | |
| 630 } | |
| 631 | |
| 632 - (void)browserActionsContainerDragged:(NSNotification*)notification { | |
| 633 CGFloat locationBarWidth = NSWidth([locationBar_ frame]); | |
| 634 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth; | |
| 635 [browserActionsContainerView_ setCanDragLeft:!locationBarAtMinSize_]; | |
| 636 [browserActionsContainerView_ setGrippyPinned:locationBarAtMinSize_]; | |
| 637 [self adjustLocationSizeBy: | |
| 638 [browserActionsContainerView_ resizeDeltaX] animate:NO]; | |
| 639 } | |
| 640 | |
| 641 - (void)browserActionsContainerDragFinished:(NSNotification*)notification { | |
| 642 [browserActionsController_ resizeContainerAndAnimate:YES]; | |
| 643 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:YES]; | |
| 644 } | |
| 645 | |
| 646 - (void)browserActionsVisibilityChanged:(NSNotification*)notification { | |
| 647 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO]; | |
| 648 } | |
| 649 | |
| 650 - (void)pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:(BOOL)animate { | |
| 651 CGFloat locationBarXPos = NSMaxX([locationBar_ frame]); | |
| 652 CGFloat leftDistance; | |
| 653 | |
| 654 if ([browserActionsContainerView_ isHidden]) { | |
| 655 CGFloat edgeXPos = [wrenchButton_ frame].origin.x; | |
| 656 leftDistance = edgeXPos - locationBarXPos - kWrenchMenuLeftPadding; | |
| 657 } else { | |
| 658 NSRect containerFrame = animate ? | |
| 659 [browserActionsContainerView_ animationEndFrame] : | |
| 660 [browserActionsContainerView_ frame]; | |
| 661 | |
| 662 leftDistance = containerFrame.origin.x - locationBarXPos; | |
| 663 } | |
| 664 if (leftDistance != 0.0) | |
| 665 [self adjustLocationSizeBy:leftDistance animate:animate]; | |
| 666 } | |
| 667 | |
| 668 - (void)maintainMinimumLocationBarWidth { | |
| 669 CGFloat locationBarWidth = NSWidth([locationBar_ frame]); | |
| 670 locationBarAtMinSize_ = locationBarWidth <= kMinimumLocationBarWidth; | |
| 671 if (locationBarAtMinSize_) { | |
| 672 CGFloat dX = kMinimumLocationBarWidth - locationBarWidth; | |
| 673 [self adjustLocationSizeBy:dX animate:NO]; | |
| 674 } | |
| 675 } | |
| 676 | |
| 677 - (void)toolbarFrameChanged { | |
| 678 // Do nothing if the frame changes but no Browser Action Controller is | |
| 679 // present. | |
| 680 if (!browserActionsController_.get()) | |
| 681 return; | |
| 682 | |
| 683 [self maintainMinimumLocationBarWidth]; | |
| 684 | |
| 685 if (locationBarAtMinSize_) { | |
| 686 // Once the grippy is pinned, leave it until it is explicity un-pinned. | |
| 687 [browserActionsContainerView_ setGrippyPinned:YES]; | |
| 688 NSRect containerFrame = [browserActionsContainerView_ frame]; | |
| 689 // Determine how much the container needs to move in case it's overlapping | |
| 690 // with the location bar. | |
| 691 CGFloat dX = NSMaxX([locationBar_ frame]) - containerFrame.origin.x; | |
| 692 containerFrame = NSOffsetRect(containerFrame, dX, 0); | |
| 693 containerFrame.size.width -= dX; | |
| 694 [browserActionsContainerView_ setFrame:containerFrame]; | |
| 695 } else if (!locationBarAtMinSize_ && | |
| 696 [browserActionsContainerView_ grippyPinned]) { | |
| 697 // Expand out the container until it hits the saved size, then unpin the | |
| 698 // grippy. | |
| 699 // Add 0.1 pixel so that it doesn't hit the minimum width codepath above. | |
| 700 CGFloat dX = NSWidth([locationBar_ frame]) - | |
| 701 (kMinimumLocationBarWidth + 0.1); | |
| 702 NSRect containerFrame = [browserActionsContainerView_ frame]; | |
| 703 containerFrame = NSOffsetRect(containerFrame, -dX, 0); | |
| 704 containerFrame.size.width += dX; | |
| 705 CGFloat savedContainerWidth = [browserActionsController_ savedWidth]; | |
| 706 if (NSWidth(containerFrame) >= savedContainerWidth) { | |
| 707 containerFrame = NSOffsetRect(containerFrame, | |
| 708 NSWidth(containerFrame) - savedContainerWidth, 0); | |
| 709 containerFrame.size.width = savedContainerWidth; | |
| 710 [browserActionsContainerView_ setGrippyPinned:NO]; | |
| 711 } | |
| 712 [browserActionsContainerView_ setFrame:containerFrame]; | |
| 713 [self pinLocationBarToLeftOfBrowserActionsContainerAndAnimate:NO]; | |
| 714 } | |
| 715 } | |
| 716 | |
| 717 - (void)adjustLocationSizeBy:(CGFloat)dX animate:(BOOL)animate { | |
| 718 // Ensure that the location bar is in its proper place. | |
| 719 NSRect locationFrame = [locationBar_ frame]; | |
| 720 locationFrame.size.width += dX; | |
| 721 | |
| 722 if (!animate) { | |
| 723 [locationBar_ setFrame:locationFrame]; | |
| 724 return; | |
| 725 } | |
| 726 | |
| 727 [NSAnimationContext beginGrouping]; | |
| 728 [[NSAnimationContext currentContext] setDuration:kAnimationDuration]; | |
| 729 [[locationBar_ animator] setFrame:locationFrame]; | |
| 730 [NSAnimationContext endGrouping]; | |
| 731 } | |
| 732 | |
| 733 - (NSPoint)bookmarkBubblePoint { | |
| 734 return locationBarView_->GetBookmarkBubblePoint(); | |
| 735 } | |
| 736 | |
| 737 - (CGFloat)desiredHeightForCompression:(CGFloat)compressByHeight { | |
| 738 // With no toolbar, just ignore the compression. | |
| 739 return hasToolbar_ ? kBaseToolbarHeight - compressByHeight : | |
| 740 NSHeight([locationBar_ frame]); | |
| 741 } | |
| 742 | |
| 743 - (void)setDividerOpacity:(CGFloat)opacity { | |
| 744 BackgroundGradientView* view = [self backgroundGradientView]; | |
| 745 [view setShowsDivider:(opacity > 0 ? YES : NO)]; | |
| 746 | |
| 747 // We may not have a toolbar view (e.g., popup windows only have a location | |
| 748 // bar). | |
| 749 if ([view isKindOfClass:[ToolbarView class]]) { | |
| 750 ToolbarView* toolbarView = (ToolbarView*)view; | |
| 751 [toolbarView setDividerOpacity:opacity]; | |
| 752 } | |
| 753 } | |
| 754 | |
| 755 - (BrowserActionsController*)browserActionsController { | |
| 756 return browserActionsController_.get(); | |
| 757 } | |
| 758 | |
| 759 // (URLDropTargetController protocol) | |
| 760 - (void)dropURLs:(NSArray*)urls inView:(NSView*)view at:(NSPoint)point { | |
| 761 // TODO(viettrungluu): This code is more or less copied from the code in | |
| 762 // |TabStripController|. I'll refactor this soon to make it common and expand | |
| 763 // its capabilities (e.g., allow text DnD). | |
| 764 if ([urls count] < 1) { | |
| 765 NOTREACHED(); | |
| 766 return; | |
| 767 } | |
| 768 | |
| 769 // TODO(viettrungluu): dropping multiple URLs? | |
| 770 if ([urls count] > 1) | |
| 771 NOTIMPLEMENTED(); | |
| 772 | |
| 773 // Get the first URL and fix it up. | |
| 774 GURL url(URLFixerUpper::FixupURL( | |
| 775 base::SysNSStringToUTF8([urls objectAtIndex:0]), std::string())); | |
| 776 | |
| 777 browser_->GetSelectedTabContents()->OpenURL(url, GURL(), CURRENT_TAB, | |
| 778 PageTransition::TYPED); | |
| 779 } | |
| 780 | |
| 781 // (URLDropTargetController protocol) | |
| 782 - (void)dropText:(NSString*)text inView:(NSView*)view at:(NSPoint)point { | |
| 783 // TODO(viettrungluu): This code is more or less copied from the code in | |
| 784 // |TabStripController|. I'll refactor this soon to make it common and expand | |
| 785 // its capabilities (e.g., allow text DnD). | |
| 786 | |
| 787 // If the input is plain text, classify the input and make the URL. | |
| 788 AutocompleteMatch match; | |
| 789 browser_->profile()->GetAutocompleteClassifier()->Classify( | |
| 790 base::SysNSStringToWide(text), | |
| 791 std::wstring(), false, &match, NULL); | |
| 792 GURL url(match.destination_url); | |
| 793 | |
| 794 browser_->GetSelectedTabContents()->OpenURL(url, GURL(), CURRENT_TAB, | |
| 795 PageTransition::TYPED); | |
| 796 } | |
| 797 | |
| 798 // (URLDropTargetController protocol) | |
| 799 - (void)indicateDropURLsInView:(NSView*)view at:(NSPoint)point { | |
| 800 // Do nothing. | |
| 801 } | |
| 802 | |
| 803 // (URLDropTargetController protocol) | |
| 804 - (void)hideDropURLsIndicatorInView:(NSView*)view { | |
| 805 // Do nothing. | |
| 806 } | |
| 807 | |
| 808 @end | |
| OLD | NEW |