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. 4 5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" 6 7 #include <algorithm> 8 #include <limits> 9 #include <set> 10 #include <vector> 11 12 #include "apps/app_launcher.h" 13 #include "base/bind.h" 14 #include "base/i18n/rtl.h" 15 #include "base/metrics/histogram.h" 16 #include "base/prefs/pref_service.h" 17 #include "base/strings/string_util.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "chrome/browser/bookmarks/bookmark_model.h" 20 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 21 #include "chrome/browser/bookmarks/bookmark_utils.h" 22 #include "chrome/browser/browser_process.h" 23 #include "chrome/browser/browser_shutdown.h" 24 #include "chrome/browser/chrome_notification_types.h" 25 #include "chrome/browser/defaults.h" 26 #include "chrome/browser/extensions/extension_service.h" 27 #include "chrome/browser/profiles/profile.h" 28 #include "chrome/browser/search/search.h" 29 #include "chrome/browser/sync/profile_sync_service.h" 30 #include "chrome/browser/sync/profile_sync_service_factory.h" 31 #include "chrome/browser/themes/theme_properties.h" 32 #include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h" 33 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h" 34 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h" 35 #include "chrome/browser/ui/bookmarks/bookmark_utils.h" 36 #include "chrome/browser/ui/browser.h" 37 #include "chrome/browser/ui/chrome_pages.h" 38 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 39 #include "chrome/browser/ui/omnibox/omnibox_view.h" 40 #include "chrome/browser/ui/tabs/tab_strip_model.h" 41 #include "chrome/browser/ui/view_ids.h" 42 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h" 43 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h" 44 #include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h" 45 #include "chrome/browser/ui/views/event_utils.h" 46 #include "chrome/browser/ui/views/frame/browser_view.h" 47 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 48 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h" 49 #include "chrome/common/chrome_switches.h" 50 #include "chrome/common/extensions/extension_constants.h" 51 #include "chrome/common/pref_names.h" 52 #include "chrome/common/url_constants.h" 53 #include "content/public/browser/notification_details.h" 54 #include "content/public/browser/notification_source.h" 55 #include "content/public/browser/page_navigator.h" 56 #include "content/public/browser/render_view_host.h" 57 #include "content/public/browser/render_widget_host_view.h" 58 #include "content/public/browser/user_metrics.h" 59 #include "content/public/browser/web_contents.h" 60 #include "content/public/common/page_transition_types.h" 61 #include "grit/generated_resources.h" 62 #include "grit/theme_resources.h" 63 #include "grit/ui_resources.h" 64 #include "ui/base/accessibility/accessible_view_state.h" 65 #include "ui/base/animation/slide_animation.h" 66 #include "ui/base/dragdrop/drag_utils.h" 67 #include "ui/base/dragdrop/os_exchange_data.h" 68 #include "ui/base/l10n/l10n_util.h" 69 #include "ui/base/resource/resource_bundle.h" 70 #include "ui/base/text/text_elider.h" 71 #include "ui/base/theme_provider.h" 72 #include "ui/base/window_open_disposition.h" 73 #include "ui/gfx/canvas.h" 74 #include "ui/views/button_drag_utils.h" 75 #include "ui/views/controls/button/menu_button.h" 76 #include "ui/views/controls/label.h" 77 #include "ui/views/controls/menu/menu_item_view.h" 78 #include "ui/views/drag_utils.h" 79 #include "ui/views/metrics.h" 80 #include "ui/views/view_constants.h" 81 #include "ui/views/widget/tooltip_manager.h" 82 #include "ui/views/widget/widget.h" 83 #include "ui/views/window/non_client_view.h" 84 85 using content::OpenURLParams; 86 using content::PageNavigator; 87 using content::Referrer; 88 using content::UserMetricsAction; 89 using ui::DropTargetEvent; 90 using views::CustomButton; 91 using views::MenuButton; 92 using views::MenuItemView; 93 using views::View; 94 95 // Margins around the content. 96 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the 97 // toolbar above serve as the margin. 98 static const int kBottomMargin = 2; 99 static const int kLeftMargin = 1; 100 static const int kRightMargin = 1; 101 102 // static 103 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView"; 104 105 // Padding between buttons. 106 static const int kButtonPadding = 0; 107 108 // Icon to display when one isn't found for the page. 109 static gfx::ImageSkia* kDefaultFavicon = NULL; 110 111 // Icon used for folders. 112 static gfx::ImageSkia* kFolderIcon = NULL; 113 114 // Offset for where the menu is shown relative to the bottom of the 115 // BookmarkBarView. 116 static const int kMenuOffset = 3; 117 118 // Color of the drop indicator. 119 static const SkColor kDropIndicatorColor = SK_ColorBLACK; 120 121 // Width of the drop indicator. 122 static const int kDropIndicatorWidth = 2; 123 124 // Distance between the bottom of the bar and the separator. 125 static const int kSeparatorMargin = 1; 126 127 // Width of the separator between the recently bookmarked button and the 128 // overflow indicator. 129 static const int kSeparatorWidth = 4; 130 131 // Starting x-coordinate of the separator line within a separator. 132 static const int kSeparatorStartX = 2; 133 134 // Left-padding for the instructional text. 135 static const int kInstructionsPadding = 6; 136 137 // Tag for the 'Other bookmarks' button. 138 static const int kOtherFolderButtonTag = 1; 139 140 // Tag for the 'Apps Shortcut' button. 141 static const int kAppsShortcutButtonTag = 2; 142 143 namespace { 144 145 // To enable/disable BookmarkBar animations during testing. In production 146 // animations are enabled by default. 147 bool animations_enabled = true; 148 149 // BookmarkButtonBase ----------------------------------------------- 150 151 // Base class for text buttons used on the bookmark bar. 152 153 class BookmarkButtonBase : public views::TextButton { 154 public: 155 BookmarkButtonBase(views::ButtonListener* listener, 156 const string16& title) 157 : TextButton(listener, title) { 158 show_animation_.reset(new ui::SlideAnimation(this)); 159 if (!animations_enabled) { 160 // For some reason during testing the events generated by animating 161 // throw off the test. So, don't animate while testing. 162 show_animation_->Reset(1); 163 } else { 164 show_animation_->Show(); 165 } 166 } 167 168 virtual bool IsTriggerableEvent(const ui::Event& e) OVERRIDE { 169 return e.type() == ui::ET_GESTURE_TAP || 170 e.type() == ui::ET_GESTURE_TAP_DOWN || 171 event_utils::IsPossibleDispositionEvent(e); 172 } 173 174 private: 175 scoped_ptr<ui::SlideAnimation> show_animation_; 176 177 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase); 178 }; 179 180 // BookmarkButton ------------------------------------------------------------- 181 182 // Buttons used for the bookmarks on the bookmark bar. 183 184 class BookmarkButton : public BookmarkButtonBase { 185 public: 186 // The internal view class name. 187 static const char kViewClassName[]; 188 189 BookmarkButton(views::ButtonListener* listener, 190 const GURL& url, 191 const string16& title, 192 Profile* profile) 193 : BookmarkButtonBase(listener, title), 194 url_(url), 195 profile_(profile) { 196 } 197 198 virtual bool GetTooltipText(const gfx::Point& p, 199 string16* tooltip) const OVERRIDE { 200 gfx::Point location(p); 201 ConvertPointToScreen(this, &location); 202 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle( 203 location, url_, text(), profile_, GetWidget()->GetNativeView()); 204 return !tooltip->empty(); 205 } 206 207 virtual const char* GetClassName() const OVERRIDE { 208 return kViewClassName; 209 } 210 211 private: 212 const GURL& url_; 213 Profile* profile_; 214 215 DISALLOW_COPY_AND_ASSIGN(BookmarkButton); 216 }; 217 218 // static 219 const char BookmarkButton::kViewClassName[] = "BookmarkButton"; 220 221 // ShortcutButton ------------------------------------------------------------- 222 223 // Buttons used for the shortcuts on the bookmark bar. 224 225 class ShortcutButton : public BookmarkButtonBase { 226 public: 227 // The internal view class name. 228 static const char kViewClassName[]; 229 230 ShortcutButton(views::ButtonListener* listener, 231 const string16& title) 232 : BookmarkButtonBase(listener, title) { 233 } 234 235 virtual const char* GetClassName() const OVERRIDE { 236 return kViewClassName; 237 } 238 239 private: 240 241 DISALLOW_COPY_AND_ASSIGN(ShortcutButton); 242 }; 243 244 // static 245 const char ShortcutButton::kViewClassName[] = "ShortcutButton"; 246 247 // BookmarkFolderButton ------------------------------------------------------- 248 249 // Buttons used for folders on the bookmark bar, including the 'other folders' 250 // button. 251 class BookmarkFolderButton : public views::MenuButton { 252 public: 253 BookmarkFolderButton(views::ButtonListener* listener, 254 const string16& title, 255 views::MenuButtonListener* menu_button_listener, 256 bool show_menu_marker) 257 : MenuButton(listener, title, menu_button_listener, show_menu_marker) { 258 show_animation_.reset(new ui::SlideAnimation(this)); 259 if (!animations_enabled) { 260 // For some reason during testing the events generated by animating 261 // throw off the test. So, don't animate while testing. 262 show_animation_->Reset(1); 263 } else { 264 show_animation_->Show(); 265 } 266 } 267 268 virtual bool GetTooltipText(const gfx::Point& p, 269 string16* tooltip) const OVERRIDE { 270 if (text_size_.width() > GetTextBounds().width()) 271 *tooltip = text_; 272 return !tooltip->empty(); 273 } 274 275 virtual bool IsTriggerableEvent(const ui::Event& e) OVERRIDE { 276 // Left clicks and taps should show the menu contents and right clicks 277 // should show the context menu. They should not trigger the opening of 278 // underlying urls. 279 if (e.type() == ui::ET_GESTURE_TAP || 280 (e.IsMouseEvent() && (e.flags() & 281 (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON)))) 282 return false; 283 284 if (e.IsMouseEvent()) 285 return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB; 286 return false; 287 } 288 289 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 290 views::MenuButton::PaintButton(canvas, views::MenuButton::PB_NORMAL); 291 } 292 293 private: 294 scoped_ptr<ui::SlideAnimation> show_animation_; 295 296 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton); 297 }; 298 299 // OverFlowButton (chevron) -------------------------------------------------- 300 301 class OverFlowButton : public views::MenuButton { 302 public: 303 explicit OverFlowButton(BookmarkBarView* owner) 304 : MenuButton(NULL, string16(), owner, false), 305 owner_(owner) {} 306 307 virtual bool OnMousePressed(const ui::MouseEvent& e) OVERRIDE { 308 owner_->StopThrobbing(true); 309 return views::MenuButton::OnMousePressed(e); 310 } 311 312 private: 313 BookmarkBarView* owner_; 314 315 DISALLOW_COPY_AND_ASSIGN(OverFlowButton); 316 }; 317 318 void RecordAppLaunch(Profile* profile, GURL url) { 319 DCHECK(profile->GetExtensionService()); 320 const extensions::Extension* extension = 321 profile->GetExtensionService()->GetInstalledApp(url); 322 if (!extension) 323 return; 324 325 CoreAppLauncherHandler::RecordAppLaunchType( 326 extension_misc::APP_LAUNCH_BOOKMARK_BAR, 327 extension->GetType()); 328 } 329 330 } // namespace 331 332 // DropLocation --------------------------------------------------------------- 333 334 struct BookmarkBarView::DropLocation { 335 DropLocation() 336 : index(-1), 337 operation(ui::DragDropTypes::DRAG_NONE), 338 on(false), 339 button_type(DROP_BOOKMARK) { 340 } 341 342 bool Equals(const DropLocation& other) { 343 return ((other.index == index) && (other.on == on) && 344 (other.button_type == button_type)); 345 } 346 347 // Index into the model the drop is over. This is relative to the root node. 348 int index; 349 350 // Drop constants. 351 int operation; 352 353 // If true, the user is dropping on a folder. 354 bool on; 355 356 // Type of button. 357 DropButtonType button_type; 358 }; 359 360 // DropInfo ------------------------------------------------------------------- 361 362 // Tracks drops on the BookmarkBarView. 363 364 struct BookmarkBarView::DropInfo { 365 DropInfo() 366 : valid(false), 367 is_menu_showing(false), 368 x(0), 369 y(0) { 370 } 371 372 // Whether the data is valid. 373 bool valid; 374 375 // If true, the menu is being shown. 376 bool is_menu_showing; 377 378 // Coordinates of the drag (in terms of the BookmarkBarView). 379 int x; 380 int y; 381 382 // DropData for the drop. 383 BookmarkNodeData data; 384 385 DropLocation location; 386 }; 387 388 // ButtonSeparatorView -------------------------------------------------------- 389 390 class BookmarkBarView::ButtonSeparatorView : public views::View { 391 public: 392 ButtonSeparatorView() {} 393 virtual ~ButtonSeparatorView() {} 394 395 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 396 DetachableToolbarView::PaintVerticalDivider( 397 canvas, kSeparatorStartX, height(), 1, 398 DetachableToolbarView::kEdgeDividerColor, 399 DetachableToolbarView::kMiddleDividerColor, 400 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR)); 401 } 402 403 virtual gfx::Size GetPreferredSize() OVERRIDE { 404 // We get the full height of the bookmark bar, so that the height returned 405 // here doesn't matter. 406 return gfx::Size(kSeparatorWidth, 1); 407 } 408 409 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE { 410 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR); 411 state->role = ui::AccessibilityTypes::ROLE_SEPARATOR; 412 } 413 414 private: 415 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView); 416 }; 417 418 // BookmarkBarView ------------------------------------------------------------ 419 420 // static 421 const int BookmarkBarView::kMaxButtonWidth = 150; 422 const int BookmarkBarView::kNewtabHorizontalPadding = 2; 423 const int BookmarkBarView::kToolbarAttachedBookmarkBarOverlap = 3; 424 425 static const gfx::ImageSkia& GetDefaultFavicon() { 426 if (!kDefaultFavicon) { 427 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 428 kDefaultFavicon = rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON); 429 } 430 return *kDefaultFavicon; 431 } 432 433 static const gfx::ImageSkia& GetFolderIcon() { 434 if (!kFolderIcon) { 435 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 436 kFolderIcon = rb.GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER); 437 } 438 return *kFolderIcon; 439 } 440 441 BookmarkBarView::BookmarkBarView(Browser* browser, BrowserView* browser_view) 442 : page_navigator_(NULL), 443 model_(NULL), 444 bookmark_menu_(NULL), 445 bookmark_drop_menu_(NULL), 446 other_bookmarked_button_(NULL), 447 apps_page_shortcut_(NULL), 448 show_folder_method_factory_(this), 449 overflow_button_(NULL), 450 instructions_(NULL), 451 bookmarks_separator_view_(NULL), 452 browser_(browser), 453 browser_view_(browser_view), 454 infobar_visible_(false), 455 throbbing_view_(NULL), 456 bookmark_bar_state_(BookmarkBar::SHOW), 457 animating_detached_(false) { 458 set_id(VIEW_ID_BOOKMARK_BAR); 459 Init(); 460 461 size_animation_->Reset(1); 462 } 463 464 BookmarkBarView::~BookmarkBarView() { 465 if (model_) 466 model_->RemoveObserver(this); 467 468 // It's possible for the menu to outlive us, reset the observer to make sure 469 // it doesn't have a reference to us. 470 if (bookmark_menu_) { 471 bookmark_menu_->set_observer(NULL); 472 bookmark_menu_->SetPageNavigator(NULL); 473 } 474 if (context_menu_.get()) 475 context_menu_->SetPageNavigator(NULL); 476 477 StopShowFolderDropMenuTimer(); 478 } 479 480 // static 481 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) { 482 animations_enabled = !disabled; 483 } 484 485 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) { 486 page_navigator_ = navigator; 487 if (bookmark_menu_) 488 bookmark_menu_->SetPageNavigator(navigator); 489 if (context_menu_.get()) 490 context_menu_->SetPageNavigator(navigator); 491 } 492 493 void BookmarkBarView::SetBookmarkBarState( 494 BookmarkBar::State state, 495 BookmarkBar::AnimateChangeType animate_type) { 496 if (animate_type == BookmarkBar::ANIMATE_STATE_CHANGE && 497 animations_enabled) { 498 animating_detached_ = (state == BookmarkBar::DETACHED || 499 bookmark_bar_state_ == BookmarkBar::DETACHED); 500 if (state == BookmarkBar::SHOW) 501 size_animation_->Show(); 502 else 503 size_animation_->Hide(); 504 } else { 505 size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0); 506 } 507 bookmark_bar_state_ = state; 508 } 509 510 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const { 511 if (!infobar_visible_ && browser_->window()->IsFullscreen()) { 512 // There is no client edge to overlap when detached in fullscreen with no 513 // infobars visible. 514 return 0; 515 } 516 return views::NonClientFrameView::kClientEdgeThickness; 517 } 518 519 bool BookmarkBarView::is_animating() { 520 return size_animation_->is_animating(); 521 } 522 523 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex( 524 const gfx::Point& loc, 525 int* model_start_index) { 526 *model_start_index = 0; 527 528 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height()) 529 return NULL; 530 531 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y()); 532 533 // Check the buttons first. 534 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 535 views::View* child = child_at(i); 536 if (!child->visible()) 537 break; 538 if (child->bounds().Contains(adjusted_loc)) 539 return model_->bookmark_bar_node()->GetChild(i); 540 } 541 542 // Then the overflow button. 543 if (overflow_button_->visible() && 544 overflow_button_->bounds().Contains(adjusted_loc)) { 545 *model_start_index = GetFirstHiddenNodeIndex(); 546 return model_->bookmark_bar_node(); 547 } 548 549 // And finally the other folder. 550 if (other_bookmarked_button_->visible() && 551 other_bookmarked_button_->bounds().Contains(adjusted_loc)) { 552 return model_->other_node(); 553 } 554 555 return NULL; 556 } 557 558 views::MenuButton* BookmarkBarView::GetMenuButtonForNode( 559 const BookmarkNode* node) { 560 if (node == model_->other_node()) 561 return other_bookmarked_button_; 562 if (node == model_->bookmark_bar_node()) 563 return overflow_button_; 564 int index = model_->bookmark_bar_node()->GetIndexOf(node); 565 if (index == -1 || !node->is_folder()) 566 return NULL; 567 return static_cast<views::MenuButton*>(child_at(index)); 568 } 569 570 void BookmarkBarView::GetAnchorPositionForButton( 571 views::MenuButton* button, 572 MenuItemView::AnchorPosition* anchor) { 573 if (button == other_bookmarked_button_ || button == overflow_button_) 574 *anchor = MenuItemView::TOPRIGHT; 575 else 576 *anchor = MenuItemView::TOPLEFT; 577 } 578 579 views::MenuItemView* BookmarkBarView::GetMenu() { 580 return bookmark_menu_ ? bookmark_menu_->menu() : NULL; 581 } 582 583 views::MenuItemView* BookmarkBarView::GetContextMenu() { 584 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL; 585 } 586 587 views::MenuItemView* BookmarkBarView::GetDropMenu() { 588 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL; 589 } 590 591 void BookmarkBarView::StopThrobbing(bool immediate) { 592 if (!throbbing_view_) 593 return; 594 595 // If not immediate, cycle through 2 more complete cycles. 596 throbbing_view_->StartThrobbing(immediate ? 0 : 4); 597 throbbing_view_ = NULL; 598 } 599 600 // static 601 string16 BookmarkBarView::CreateToolTipForURLAndTitle( 602 const gfx::Point& screen_loc, 603 const GURL& url, 604 const string16& title, 605 Profile* profile, 606 gfx::NativeView context) { 607 int max_width = views::TooltipManager::GetMaxWidth(screen_loc.x(), 608 screen_loc.y(), 609 context); 610 gfx::Font tt_font = views::TooltipManager::GetDefaultFont(); 611 string16 result; 612 613 // First the title. 614 if (!title.empty()) { 615 string16 localized_title = title; 616 base::i18n::AdjustStringForLocaleDirection(&localized_title); 617 result.append(ui::ElideText(localized_title, tt_font, max_width, 618 ui::ELIDE_AT_END)); 619 } 620 621 // Only show the URL if the url and title differ. 622 if (title != UTF8ToUTF16(url.spec())) { 623 if (!result.empty()) 624 result.push_back('\n'); 625 626 // We need to explicitly specify the directionality of the URL's text to 627 // make sure it is treated as an LTR string when the context is RTL. For 628 // example, the URL "http://www.yahoo.com/" appears as 629 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since 630 // the Unicode BiDi algorithm puts certain characters on the left by 631 // default. 632 std::string languages = profile->GetPrefs()->GetString( 633 prefs::kAcceptLanguages); 634 string16 elided_url(ui::ElideUrl(url, tt_font, max_width, languages)); 635 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url); 636 result.append(elided_url); 637 } 638 return result; 639 } 640 641 bool BookmarkBarView::IsDetached() const { 642 return (bookmark_bar_state_ == BookmarkBar::DETACHED) || 643 (animating_detached_ && size_animation_->is_animating()); 644 } 645 646 double BookmarkBarView::GetAnimationValue() const { 647 return size_animation_->GetCurrentValue(); 648 } 649 650 int BookmarkBarView::GetToolbarOverlap() const { 651 int attached_overlap = kToolbarAttachedBookmarkBarOverlap + 652 views::NonClientFrameView::kClientEdgeThickness; 653 if (!IsDetached()) 654 return attached_overlap; 655 656 int detached_overlap = GetFullyDetachedToolbarOverlap(); 657 658 // Do not animate the overlap when the infobar is above us (i.e. when we're 659 // detached), since drawing over the infobar looks weird. 660 if (infobar_visible_) 661 return detached_overlap; 662 663 // When detached with no infobar, animate the overlap between the attached and 664 // detached states. 665 return detached_overlap + static_cast<int>( 666 (attached_overlap - detached_overlap) * 667 size_animation_->GetCurrentValue()); 668 } 669 670 gfx::Size BookmarkBarView::GetPreferredSize() { 671 return LayoutItems(true); 672 } 673 674 bool BookmarkBarView::HitTestRect(const gfx::Rect& rect) const { 675 // If bookmark bar is attached and omnibox popup is open (on top of the bar), 676 // force hit-testing to fail. This prevents hovers/clicks just above the 677 // omnibox popup from activating the top few pixels of items on the bookmark 678 // bar. 679 if (!IsDetached() && browser_view_ && 680 browser_view_->GetLocationBar()->GetLocationEntry()->model()-> 681 popup_model()->IsOpen()) { 682 return false; 683 } 684 return DetachableToolbarView::HitTestRect(rect); 685 } 686 687 gfx::Size BookmarkBarView::GetMinimumSize() { 688 // The minimum width of the bookmark bar should at least contain the overflow 689 // button, by which one can access all the Bookmark Bar items, and the "Other 690 // Bookmarks" folder, along with appropriate margins and button padding. 691 int width = kLeftMargin; 692 693 int height = browser_defaults::kBookmarkBarHeight; 694 if (IsDetached()) { 695 double current_state = 1 - size_animation_->GetCurrentValue(); 696 width += 2 * static_cast<int>(kNewtabHorizontalPadding * current_state); 697 height += static_cast<int>( 698 (chrome::kNTPBookmarkBarHeight - browser_defaults::kBookmarkBarHeight) * 699 current_state); 700 } 701 702 gfx::Size other_bookmarked_pref; 703 if (other_bookmarked_button_->visible()) 704 other_bookmarked_pref = other_bookmarked_button_->GetPreferredSize(); 705 gfx::Size overflow_pref; 706 if (overflow_button_->visible()) 707 overflow_pref = overflow_button_->GetPreferredSize(); 708 gfx::Size bookmarks_separator_pref; 709 if (bookmarks_separator_view_->visible()) 710 bookmarks_separator_pref = bookmarks_separator_view_->GetPreferredSize(); 711 712 gfx::Size apps_page_shortcut_pref; 713 if (apps_page_shortcut_->visible()) 714 apps_page_shortcut_pref = apps_page_shortcut_->GetPreferredSize(); 715 width += other_bookmarked_pref.width() + kButtonPadding + 716 apps_page_shortcut_pref.width() + kButtonPadding + 717 overflow_pref.width() + kButtonPadding + 718 bookmarks_separator_pref.width(); 719 720 return gfx::Size(width, height); 721 } 722 723 void BookmarkBarView::Layout() { 724 LayoutItems(false); 725 } 726 727 void BookmarkBarView::ViewHierarchyChanged( 728 const ViewHierarchyChangedDetails& details) { 729 if (details.is_add && details.child == this) { 730 // We may get inserted into a hierarchy with a profile - this typically 731 // occurs when the bar's contents get populated fast enough that the 732 // buttons are created before the bar is attached to a frame. 733 UpdateColors(); 734 735 if (height() > 0) { 736 // We only layout while parented. When we become parented, if our bounds 737 // haven't changed, OnBoundsChanged() won't get invoked and we won't 738 // layout. Therefore we always force a layout when added. 739 Layout(); 740 } 741 } 742 } 743 744 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas) { 745 View::PaintChildren(canvas); 746 747 if (drop_info_.get() && drop_info_->valid && 748 drop_info_->location.operation != 0 && drop_info_->location.index != -1 && 749 drop_info_->location.button_type != DROP_OVERFLOW && 750 !drop_info_->location.on) { 751 int index = drop_info_->location.index; 752 DCHECK(index <= GetBookmarkButtonCount()); 753 int x = 0; 754 int y = 0; 755 int h = height(); 756 if (index == GetBookmarkButtonCount()) { 757 if (index == 0) { 758 x = kLeftMargin; 759 } else { 760 x = GetBookmarkButton(index - 1)->x() + 761 GetBookmarkButton(index - 1)->width(); 762 } 763 } else { 764 x = GetBookmarkButton(index)->x(); 765 } 766 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) { 767 y = GetBookmarkButton(0)->y(); 768 h = GetBookmarkButton(0)->height(); 769 } 770 771 // Since the drop indicator is painted directly onto the canvas, we must 772 // make sure it is painted in the right location if the locale is RTL. 773 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2, 774 y, 775 kDropIndicatorWidth, 776 h); 777 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds)); 778 779 // TODO(sky/glen): make me pretty! 780 canvas->FillRect(indicator_bounds, kDropIndicatorColor); 781 } 782 } 783 784 bool BookmarkBarView::GetDropFormats( 785 int* formats, 786 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) { 787 if (!model_ || !model_->loaded()) 788 return false; 789 *formats = ui::OSExchangeData::URL; 790 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat()); 791 return true; 792 } 793 794 bool BookmarkBarView::AreDropTypesRequired() { 795 return true; 796 } 797 798 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) { 799 if (!model_ || !model_->loaded() || 800 !browser_->profile()->GetPrefs()->GetBoolean( 801 prefs::kEditBookmarksEnabled)) 802 return false; 803 804 if (!drop_info_.get()) 805 drop_info_.reset(new DropInfo()); 806 807 // Only accept drops of 1 node, which is the case for all data dragged from 808 // bookmark bar and menus. 809 return drop_info_->data.Read(data) && drop_info_->data.size() == 1; 810 } 811 812 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) { 813 } 814 815 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) { 816 if (!drop_info_.get()) 817 return 0; 818 819 if (drop_info_->valid && 820 (drop_info_->x == event.x() && drop_info_->y == event.y())) { 821 // The location of the mouse didn't change, return the last operation. 822 return drop_info_->location.operation; 823 } 824 825 drop_info_->x = event.x(); 826 drop_info_->y = event.y(); 827 828 DropLocation location; 829 CalculateDropLocation(event, drop_info_->data, &location); 830 831 if (drop_info_->valid && drop_info_->location.Equals(location)) { 832 // The position we're going to drop didn't change, return the last drag 833 // operation we calculated. Copy of the operation in case it changed. 834 drop_info_->location.operation = location.operation; 835 return drop_info_->location.operation; 836 } 837 838 StopShowFolderDropMenuTimer(); 839 840 // TODO(sky): Optimize paint region. 841 SchedulePaint(); 842 843 drop_info_->location = location; 844 drop_info_->valid = true; 845 846 if (drop_info_->is_menu_showing) { 847 if (bookmark_drop_menu_) 848 bookmark_drop_menu_->Cancel(); 849 drop_info_->is_menu_showing = false; 850 } 851 852 if (location.on || location.button_type == DROP_OVERFLOW || 853 location.button_type == DROP_OTHER_FOLDER) { 854 const BookmarkNode* node; 855 if (location.button_type == DROP_OTHER_FOLDER) 856 node = model_->other_node(); 857 else if (location.button_type == DROP_OVERFLOW) 858 node = model_->bookmark_bar_node(); 859 else 860 node = model_->bookmark_bar_node()->GetChild(location.index); 861 StartShowFolderDropMenuTimer(node); 862 } 863 864 return drop_info_->location.operation; 865 } 866 867 void BookmarkBarView::OnDragExited() { 868 StopShowFolderDropMenuTimer(); 869 870 // NOTE: we don't hide the menu on exit as it's possible the user moved the 871 // mouse over the menu, which triggers an exit on us. 872 873 drop_info_->valid = false; 874 875 if (drop_info_->location.index != -1) { 876 // TODO(sky): optimize the paint region. 877 SchedulePaint(); 878 } 879 drop_info_.reset(); 880 } 881 882 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) { 883 StopShowFolderDropMenuTimer(); 884 885 if (bookmark_drop_menu_) 886 bookmark_drop_menu_->Cancel(); 887 888 if (!drop_info_.get() || !drop_info_->location.operation) 889 return ui::DragDropTypes::DRAG_NONE; 890 891 const BookmarkNode* root = 892 (drop_info_->location.button_type == DROP_OTHER_FOLDER) ? 893 model_->other_node() : model_->bookmark_bar_node(); 894 int index = drop_info_->location.index; 895 896 if (index != -1) { 897 // TODO(sky): optimize the SchedulePaint region. 898 SchedulePaint(); 899 } 900 const BookmarkNode* parent_node; 901 if (drop_info_->location.button_type == DROP_OTHER_FOLDER) { 902 parent_node = root; 903 index = parent_node->child_count(); 904 } else if (drop_info_->location.on) { 905 parent_node = root->GetChild(index); 906 index = parent_node->child_count(); 907 } else { 908 parent_node = root; 909 } 910 const BookmarkNodeData data = drop_info_->data; 911 DCHECK(data.is_valid()); 912 drop_info_.reset(); 913 return chrome::DropBookmarks(browser_->profile(), data, parent_node, index); 914 } 915 916 void BookmarkBarView::OnThemeChanged() { 917 UpdateColors(); 918 } 919 920 const char* BookmarkBarView::GetClassName() const { 921 return kViewClassName; 922 } 923 924 void BookmarkBarView::GetAccessibleState(ui::AccessibleViewState* state) { 925 state->role = ui::AccessibilityTypes::ROLE_TOOLBAR; 926 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS); 927 } 928 929 void BookmarkBarView::AnimationProgressed(const ui::Animation* animation) { 930 // |browser_view_| can be NULL during tests. 931 if (browser_view_) 932 browser_view_->ToolbarSizeChanged(true); 933 } 934 935 void BookmarkBarView::AnimationEnded(const ui::Animation* animation) { 936 // |browser_view_| can be NULL during tests. 937 if (browser_view_) { 938 browser_view_->ToolbarSizeChanged(false); 939 SchedulePaint(); 940 } 941 } 942 943 void BookmarkBarView::BookmarkMenuDeleted(BookmarkMenuController* controller) { 944 if (controller == bookmark_menu_) 945 bookmark_menu_ = NULL; 946 else if (controller == bookmark_drop_menu_) 947 bookmark_drop_menu_ = NULL; 948 } 949 950 void BookmarkBarView::ShowImportDialog() { 951 int64 install_time = 952 g_browser_process->local_state()->GetInt64(prefs::kInstallDate); 953 int64 time_from_install = base::Time::Now().ToTimeT() - install_time; 954 if (bookmark_bar_state_ == BookmarkBar::SHOW) { 955 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView", 956 time_from_install); 957 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) { 958 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView", 959 time_from_install); 960 } 961 962 chrome::ShowImportDialog(browser_); 963 } 964 965 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) { 966 StopThrobbing(true); 967 const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url); 968 if (!node) 969 return; // Generally shouldn't happen. 970 StartThrobbing(node, false); 971 } 972 973 void BookmarkBarView::OnBookmarkBubbleHidden() { 974 StopThrobbing(false); 975 } 976 977 void BookmarkBarView::Loaded(BookmarkModel* model, bool ids_reassigned) { 978 // There should be no buttons. If non-zero it means Load was invoked more than 979 // once, or we didn't properly clear things. Either of which shouldn't happen. 980 DCHECK_EQ(0, GetBookmarkButtonCount()); 981 const BookmarkNode* node = model_->bookmark_bar_node(); 982 DCHECK(node); 983 // Create a button for each of the children on the bookmark bar. 984 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) 985 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i); 986 DCHECK(model_->other_node()); 987 other_bookmarked_button_->SetAccessibleName(model_->other_node()->GetTitle()); 988 other_bookmarked_button_->SetText(model_->other_node()->GetTitle()); 989 UpdateColors(); 990 UpdateOtherBookmarksVisibility(); 991 other_bookmarked_button_->SetEnabled(true); 992 993 Layout(); 994 SchedulePaint(); 995 } 996 997 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) { 998 // In normal shutdown The bookmark model should never be deleted before us. 999 // When X exits suddenly though, it can happen, This code exists 1000 // to check for regressions in shutdown code and not crash. 1001 if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers()) 1002 NOTREACHED(); 1003 1004 // Do minimal cleanup, presumably we'll be deleted shortly. 1005 model_->RemoveObserver(this); 1006 model_ = NULL; 1007 } 1008 1009 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model, 1010 const BookmarkNode* old_parent, 1011 int old_index, 1012 const BookmarkNode* new_parent, 1013 int new_index) { 1014 bool was_throbbing = throbbing_view_ && 1015 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index); 1016 if (was_throbbing) 1017 throbbing_view_->StopThrobbing(); 1018 BookmarkNodeRemovedImpl(model, old_parent, old_index); 1019 BookmarkNodeAddedImpl(model, new_parent, new_index); 1020 if (was_throbbing) 1021 StartThrobbing(new_parent->GetChild(new_index), false); 1022 } 1023 1024 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model, 1025 const BookmarkNode* parent, 1026 int index) { 1027 BookmarkNodeAddedImpl(model, parent, index); 1028 } 1029 1030 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model, 1031 const BookmarkNode* parent, 1032 int old_index, 1033 const BookmarkNode* node) { 1034 // Close the menu if the menu is showing for the deleted node. 1035 if (bookmark_menu_ && bookmark_menu_->node() == node) 1036 bookmark_menu_->Cancel(); 1037 BookmarkNodeRemovedImpl(model, parent, old_index); 1038 } 1039 1040 void BookmarkBarView::BookmarkAllNodesRemoved(BookmarkModel* model) { 1041 UpdateOtherBookmarksVisibility(); 1042 1043 StopThrobbing(true); 1044 1045 // Remove the existing buttons. 1046 while (GetBookmarkButtonCount()) { 1047 delete GetBookmarkButton(0); 1048 } 1049 1050 Layout(); 1051 SchedulePaint(); 1052 } 1053 1054 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model, 1055 const BookmarkNode* node) { 1056 BookmarkNodeChangedImpl(model, node); 1057 } 1058 1059 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model, 1060 const BookmarkNode* node) { 1061 if (node != model_->bookmark_bar_node()) 1062 return; // We only care about reordering of the bookmark bar node. 1063 1064 // Remove the existing buttons. 1065 while (GetBookmarkButtonCount()) { 1066 views::View* button = child_at(0); 1067 RemoveChildView(button); 1068 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button); 1069 } 1070 1071 // Create the new buttons. 1072 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) 1073 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i); 1074 UpdateColors(); 1075 1076 Layout(); 1077 SchedulePaint(); 1078 } 1079 1080 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model, 1081 const BookmarkNode* node) { 1082 BookmarkNodeChangedImpl(model, node); 1083 } 1084 1085 void BookmarkBarView::WriteDragDataForView(View* sender, 1086 const gfx::Point& press_pt, 1087 ui::OSExchangeData* data) { 1088 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton")); 1089 1090 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1091 if (sender == GetBookmarkButton(i)) { 1092 views::TextButton* button = GetBookmarkButton(i); 1093 scoped_ptr<gfx::Canvas> canvas( 1094 views::GetCanvasForDragImage(button->GetWidget(), button->size())); 1095 button->PaintButton(canvas.get(), views::TextButton::PB_FOR_DRAG); 1096 drag_utils::SetDragImageOnDataObject(*canvas, button->size(), 1097 press_pt.OffsetFromOrigin(), 1098 data); 1099 WriteBookmarkDragData(model_->bookmark_bar_node()->GetChild(i), data); 1100 return; 1101 } 1102 } 1103 NOTREACHED(); 1104 } 1105 1106 int BookmarkBarView::GetDragOperationsForView(View* sender, 1107 const gfx::Point& p) { 1108 if (size_animation_->is_animating() || 1109 (size_animation_->GetCurrentValue() == 0 && 1110 bookmark_bar_state_ != BookmarkBar::DETACHED)) { 1111 // Don't let the user drag while animating open or we're closed (and not 1112 // detached, when detached size_animation_ is always 0). This typically is 1113 // only hit if the user does something to inadvertently trigger DnD such as 1114 // pressing the mouse and hitting control-b. 1115 return ui::DragDropTypes::DRAG_NONE; 1116 } 1117 1118 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1119 if (sender == GetBookmarkButton(i)) { 1120 return chrome::GetBookmarkDragOperation( 1121 browser_->profile(), model_->bookmark_bar_node()->GetChild(i)); 1122 } 1123 } 1124 NOTREACHED(); 1125 return ui::DragDropTypes::DRAG_NONE; 1126 } 1127 1128 bool BookmarkBarView::CanStartDragForView(views::View* sender, 1129 const gfx::Point& press_pt, 1130 const gfx::Point& p) { 1131 // Check if we have not moved enough horizontally but we have moved downward 1132 // vertically - downward drag. 1133 gfx::Vector2d move_offset = p - press_pt; 1134 gfx::Vector2d horizontal_offset(move_offset.x(), 0); 1135 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) { 1136 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1137 if (sender == GetBookmarkButton(i)) { 1138 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i); 1139 // If the folder button was dragged, show the menu instead. 1140 if (node && node->is_folder()) { 1141 views::MenuButton* menu_button = 1142 static_cast<views::MenuButton*>(sender); 1143 menu_button->Activate(); 1144 return false; 1145 } 1146 break; 1147 } 1148 } 1149 } 1150 return true; 1151 } 1152 1153 void BookmarkBarView::OnMenuButtonClicked(views::View* view, 1154 const gfx::Point& point) { 1155 const BookmarkNode* node; 1156 1157 int start_index = 0; 1158 if (view == other_bookmarked_button_) { 1159 node = model_->other_node(); 1160 } else if (view == overflow_button_) { 1161 node = model_->bookmark_bar_node(); 1162 start_index = GetFirstHiddenNodeIndex(); 1163 } else { 1164 int button_index = GetIndexOf(view); 1165 DCHECK_NE(-1, button_index); 1166 node = model_->bookmark_bar_node()->GetChild(button_index); 1167 } 1168 1169 bookmark_utils::RecordBookmarkFolderOpen(GetBookmarkLaunchLocation()); 1170 bookmark_menu_ = new BookmarkMenuController(browser_, 1171 page_navigator_, GetWidget(), node, start_index); 1172 bookmark_menu_->set_observer(this); 1173 bookmark_menu_->RunMenuAt(this, false); 1174 } 1175 1176 void BookmarkBarView::ButtonPressed(views::Button* sender, 1177 const ui::Event& event) { 1178 WindowOpenDisposition disposition_from_event_flags = 1179 ui::DispositionFromEventFlags(event.flags()); 1180 1181 if (sender->tag() == kAppsShortcutButtonTag) { 1182 OpenURLParams params(GURL(chrome::kChromeUIAppsURL), 1183 Referrer(), 1184 disposition_from_event_flags, 1185 content::PAGE_TRANSITION_AUTO_BOOKMARK, 1186 false); 1187 page_navigator_->OpenURL(params); 1188 bookmark_utils::RecordAppsPageOpen(GetBookmarkLaunchLocation()); 1189 return; 1190 } 1191 1192 const BookmarkNode* node; 1193 if (sender->tag() == kOtherFolderButtonTag) { 1194 node = model_->other_node(); 1195 } else { 1196 int index = GetIndexOf(sender); 1197 DCHECK_NE(-1, index); 1198 node = model_->bookmark_bar_node()->GetChild(index); 1199 } 1200 DCHECK(page_navigator_); 1201 1202 if (node->is_url()) { 1203 RecordAppLaunch(browser_->profile(), node->url()); 1204 OpenURLParams params( 1205 node->url(), Referrer(), disposition_from_event_flags, 1206 content::PAGE_TRANSITION_AUTO_BOOKMARK, false); 1207 page_navigator_->OpenURL(params); 1208 } else { 1209 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node, 1210 disposition_from_event_flags, browser_->profile()); 1211 } 1212 1213 bookmark_utils::RecordBookmarkLaunch(GetBookmarkLaunchLocation()); 1214 } 1215 1216 void BookmarkBarView::ShowContextMenuForView(views::View* source, 1217 const gfx::Point& point, 1218 ui::MenuSourceType source_type) { 1219 if (!model_->loaded()) { 1220 // Don't do anything if the model isn't loaded. 1221 return; 1222 } 1223 1224 const BookmarkNode* parent = NULL; 1225 std::vector<const BookmarkNode*> nodes; 1226 if (source == other_bookmarked_button_) { 1227 parent = model_->other_node(); 1228 // Do this so the user can open all bookmarks. BookmarkContextMenu makes 1229 // sure the user can't edit/delete the node in this case. 1230 nodes.push_back(parent); 1231 } else if (source != this && source != apps_page_shortcut_) { 1232 // User clicked on one of the bookmark buttons, find which one they 1233 // clicked on, except for the apps page shortcut, which must behave as if 1234 // the user clicked on the bookmark bar background. 1235 int bookmark_button_index = GetIndexOf(source); 1236 DCHECK(bookmark_button_index != -1 && 1237 bookmark_button_index < GetBookmarkButtonCount()); 1238 const BookmarkNode* node = 1239 model_->bookmark_bar_node()->GetChild(bookmark_button_index); 1240 nodes.push_back(node); 1241 parent = node->parent(); 1242 } else { 1243 parent = model_->bookmark_bar_node(); 1244 nodes.push_back(parent); 1245 } 1246 Profile* profile = browser_->profile(); 1247 bool close_on_remove = 1248 (parent == BookmarkModelFactory::GetForProfile(profile)->other_node()) && 1249 (parent->child_count() == 1); 1250 context_menu_.reset(new BookmarkContextMenu( 1251 GetWidget(), browser_, profile, 1252 browser_->tab_strip_model()->GetActiveWebContents(), 1253 parent, nodes, close_on_remove)); 1254 context_menu_->RunMenuAt(point, source_type); 1255 } 1256 1257 void BookmarkBarView::Init() { 1258 // Note that at this point we're not in a hierarchy so GetThemeProvider() will 1259 // return NULL. When we're inserted into a hierarchy, we'll call 1260 // UpdateColors(), which will set the appropriate colors for all the objects 1261 // added in this function. 1262 1263 // Child views are traversed in the order they are added. Make sure the order 1264 // they are added matches the visual order. 1265 overflow_button_ = CreateOverflowButton(); 1266 AddChildView(overflow_button_); 1267 1268 other_bookmarked_button_ = CreateOtherBookmarkedButton(); 1269 // We'll re-enable when the model is loaded. 1270 other_bookmarked_button_->SetEnabled(false); 1271 AddChildView(other_bookmarked_button_); 1272 1273 apps_page_shortcut_ = CreateAppsPageShortcutButton(); 1274 AddChildView(apps_page_shortcut_); 1275 profile_pref_registrar_.Init(browser_->profile()->GetPrefs()); 1276 profile_pref_registrar_.Add( 1277 prefs::kShowAppsShortcutInBookmarkBar, 1278 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged, 1279 base::Unretained(this))); 1280 apps_page_shortcut_->SetVisible( 1281 chrome::ShouldShowAppsShortcutInBookmarkBar(browser_->profile())); 1282 1283 bookmarks_separator_view_ = new ButtonSeparatorView(); 1284 AddChildView(bookmarks_separator_view_); 1285 UpdateBookmarksSeparatorVisibility(); 1286 1287 instructions_ = new BookmarkBarInstructionsView(this); 1288 AddChildView(instructions_); 1289 1290 set_context_menu_controller(this); 1291 1292 size_animation_.reset(new ui::SlideAnimation(this)); 1293 1294 model_ = BookmarkModelFactory::GetForProfile(browser_->profile()); 1295 if (model_) { 1296 model_->AddObserver(this); 1297 if (model_->loaded()) 1298 Loaded(model_, false); 1299 // else case: we'll receive notification back from the BookmarkModel when 1300 // done loading, then we'll populate the bar. 1301 } 1302 } 1303 1304 int BookmarkBarView::GetBookmarkButtonCount() { 1305 // We contain four non-bookmark button views: other bookmarks, bookmarks 1306 // separator, chevrons (for overflow), apps page, and the instruction label. 1307 return child_count() - 5; 1308 } 1309 1310 views::TextButton* BookmarkBarView::GetBookmarkButton(int index) { 1311 DCHECK(index >= 0 && index < GetBookmarkButtonCount()); 1312 return static_cast<views::TextButton*>(child_at(index)); 1313 } 1314 1315 bookmark_utils::BookmarkLaunchLocation 1316 BookmarkBarView::GetBookmarkLaunchLocation() const { 1317 return IsDetached() ? bookmark_utils::LAUNCH_DETACHED_BAR : 1318 bookmark_utils::LAUNCH_ATTACHED_BAR; 1319 } 1320 1321 int BookmarkBarView::GetFirstHiddenNodeIndex() { 1322 const int bb_count = GetBookmarkButtonCount(); 1323 for (int i = 0; i < bb_count; ++i) { 1324 if (!GetBookmarkButton(i)->visible()) 1325 return i; 1326 } 1327 return bb_count; 1328 } 1329 1330 MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() { 1331 // Title is set in Loaded. 1332 MenuButton* button = new BookmarkFolderButton(this, string16(), this, false); 1333 button->set_id(VIEW_ID_OTHER_BOOKMARKS); 1334 button->SetIcon(GetFolderIcon()); 1335 button->set_context_menu_controller(this); 1336 button->set_tag(kOtherFolderButtonTag); 1337 return button; 1338 } 1339 1340 MenuButton* BookmarkBarView::CreateOverflowButton() { 1341 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 1342 MenuButton* button = new OverFlowButton(this); 1343 button->SetIcon(*rb.GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS)); 1344 1345 // The overflow button's image contains an arrow and therefore it is a 1346 // direction sensitive image and we need to flip it if the UI layout is 1347 // right-to-left. 1348 // 1349 // By default, menu buttons are not flipped because they generally contain 1350 // text and flipping the gfx::Canvas object will break text rendering. Since 1351 // the overflow button does not contain text, we can safely flip it. 1352 button->EnableCanvasFlippingForRTLUI(true); 1353 1354 // Make visible as necessary. 1355 button->SetVisible(false); 1356 // Set accessibility name. 1357 button->SetAccessibleName( 1358 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON)); 1359 return button; 1360 } 1361 1362 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) { 1363 if (node->is_url()) { 1364 BookmarkButton* button = new BookmarkButton( 1365 this, node->url(), node->GetTitle(), browser_->profile()); 1366 ConfigureButton(node, button); 1367 return button; 1368 } else { 1369 views::MenuButton* button = new BookmarkFolderButton( 1370 this, node->GetTitle(), this, false); 1371 button->SetIcon(GetFolderIcon()); 1372 ConfigureButton(node, button); 1373 return button; 1374 } 1375 } 1376 1377 views::TextButton* BookmarkBarView::CreateAppsPageShortcutButton() { 1378 views::TextButton* button = new ShortcutButton( 1379 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME)); 1380 button->SetTooltipText(l10n_util::GetStringUTF16( 1381 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP)); 1382 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT); 1383 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 1384 button->SetIcon(*rb.GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT)); 1385 button->set_context_menu_controller(this); 1386 button->set_tag(kAppsShortcutButtonTag); 1387 return button; 1388 } 1389 1390 void BookmarkBarView::ConfigureButton(const BookmarkNode* node, 1391 views::TextButton* button) { 1392 button->SetText(node->GetTitle()); 1393 button->SetAccessibleName(node->GetTitle()); 1394 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT); 1395 // We don't always have a theme provider (ui tests, for example). 1396 if (GetThemeProvider()) { 1397 button->SetEnabledColor(GetThemeProvider()->GetColor( 1398 ThemeProperties::COLOR_BOOKMARK_TEXT)); 1399 } 1400 1401 button->ClearMaxTextSize(); 1402 button->set_context_menu_controller(this); 1403 button->set_drag_controller(this); 1404 if (node->is_url()) { 1405 const gfx::Image& favicon = model_->GetFavicon(node); 1406 if (!favicon.IsEmpty()) 1407 button->SetIcon(*favicon.ToImageSkia()); 1408 else 1409 button->SetIcon(GetDefaultFavicon()); 1410 } 1411 button->set_max_width(kMaxButtonWidth); 1412 } 1413 1414 void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model, 1415 const BookmarkNode* parent, 1416 int index) { 1417 UpdateOtherBookmarksVisibility(); 1418 if (parent != model_->bookmark_bar_node()) { 1419 // We only care about nodes on the bookmark bar. 1420 return; 1421 } 1422 DCHECK(index >= 0 && index <= GetBookmarkButtonCount()); 1423 const BookmarkNode* node = parent->GetChild(index); 1424 ProfileSyncService* sync_service(ProfileSyncServiceFactory:: 1425 GetInstance()->GetForProfile(browser_->profile())); 1426 if (!throbbing_view_ && sync_service && sync_service->FirstSetupInProgress()) 1427 StartThrobbing(node, true); 1428 AddChildViewAt(CreateBookmarkButton(node), index); 1429 UpdateColors(); 1430 Layout(); 1431 SchedulePaint(); 1432 } 1433 1434 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model, 1435 const BookmarkNode* parent, 1436 int index) { 1437 UpdateOtherBookmarksVisibility(); 1438 1439 StopThrobbing(true); 1440 // No need to start throbbing again as the bookmark bubble can't be up at 1441 // the same time as the user reorders. 1442 1443 if (parent != model_->bookmark_bar_node()) { 1444 // We only care about nodes on the bookmark bar. 1445 return; 1446 } 1447 DCHECK(index >= 0 && index < GetBookmarkButtonCount()); 1448 views::View* button = child_at(index); 1449 RemoveChildView(button); 1450 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button); 1451 Layout(); 1452 SchedulePaint(); 1453 } 1454 1455 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model, 1456 const BookmarkNode* node) { 1457 if (node->parent() != model_->bookmark_bar_node()) { 1458 // We only care about nodes on the bookmark bar. 1459 return; 1460 } 1461 int index = model_->bookmark_bar_node()->GetIndexOf(node); 1462 DCHECK_NE(-1, index); 1463 views::TextButton* button = GetBookmarkButton(index); 1464 gfx::Size old_pref = button->GetPreferredSize(); 1465 ConfigureButton(node, button); 1466 gfx::Size new_pref = button->GetPreferredSize(); 1467 if (old_pref.width() != new_pref.width()) { 1468 Layout(); 1469 SchedulePaint(); 1470 } else if (button->visible()) { 1471 button->SchedulePaint(); 1472 } 1473 } 1474 1475 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) { 1476 if (bookmark_drop_menu_) { 1477 if (bookmark_drop_menu_->node() == node) { 1478 // Already showing for the specified node. 1479 return; 1480 } 1481 bookmark_drop_menu_->Cancel(); 1482 } 1483 1484 views::MenuButton* menu_button = GetMenuButtonForNode(node); 1485 if (!menu_button) 1486 return; 1487 1488 int start_index = 0; 1489 if (node == model_->bookmark_bar_node()) 1490 start_index = GetFirstHiddenNodeIndex(); 1491 1492 drop_info_->is_menu_showing = true; 1493 bookmark_drop_menu_ = new BookmarkMenuController(browser_, 1494 page_navigator_, GetWidget(), node, start_index); 1495 bookmark_drop_menu_->set_observer(this); 1496 bookmark_drop_menu_->RunMenuAt(this, true); 1497 } 1498 1499 void BookmarkBarView::StopShowFolderDropMenuTimer() { 1500 show_folder_method_factory_.InvalidateWeakPtrs(); 1501 } 1502 1503 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) { 1504 if (!animations_enabled) { 1505 // So that tests can run as fast as possible disable the delay during 1506 // testing. 1507 ShowDropFolderForNode(node); 1508 return; 1509 } 1510 show_folder_method_factory_.InvalidateWeakPtrs(); 1511 base::MessageLoop::current()->PostDelayedTask( 1512 FROM_HERE, 1513 base::Bind(&BookmarkBarView::ShowDropFolderForNode, 1514 show_folder_method_factory_.GetWeakPtr(), 1515 node), 1516 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay())); 1517 } 1518 1519 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event, 1520 const BookmarkNodeData& data, 1521 DropLocation* location) { 1522 DCHECK(model_); 1523 DCHECK(model_->loaded()); 1524 DCHECK(data.is_valid()); 1525 1526 *location = DropLocation(); 1527 1528 // The drop event uses the screen coordinates while the child Views are 1529 // always laid out from left to right (even though they are rendered from 1530 // right-to-left on RTL locales). Thus, in order to make sure the drop 1531 // coordinates calculation works, we mirror the event's X coordinate if the 1532 // locale is RTL. 1533 int mirrored_x = GetMirroredXInView(event.x()); 1534 1535 bool found = false; 1536 const int other_delta_x = mirrored_x - other_bookmarked_button_->x(); 1537 Profile* profile = browser_->profile(); 1538 if (other_bookmarked_button_->visible() && other_delta_x >= 0 && 1539 other_delta_x < other_bookmarked_button_->width()) { 1540 // Mouse is over 'other' folder. 1541 location->button_type = DROP_OTHER_FOLDER; 1542 location->on = true; 1543 found = true; 1544 } else if (!GetBookmarkButtonCount()) { 1545 // No bookmarks, accept the drop. 1546 location->index = 0; 1547 int ops = data.GetFirstNode(profile) ? ui::DragDropTypes::DRAG_MOVE : 1548 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK; 1549 location->operation = chrome::GetPreferredBookmarkDropOperation( 1550 event.source_operations(), ops); 1551 return; 1552 } 1553 1554 for (int i = 0; i < GetBookmarkButtonCount() && 1555 GetBookmarkButton(i)->visible() && !found; i++) { 1556 views::TextButton* button = GetBookmarkButton(i); 1557 int button_x = mirrored_x - button->x(); 1558 int button_w = button->width(); 1559 if (button_x < button_w) { 1560 found = true; 1561 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i); 1562 if (node->is_folder()) { 1563 if (button_x <= views::kDropBetweenPixels) { 1564 location->index = i; 1565 } else if (button_x < button_w - views::kDropBetweenPixels) { 1566 location->index = i; 1567 location->on = true; 1568 } else { 1569 location->index = i + 1; 1570 } 1571 } else if (button_x < button_w / 2) { 1572 location->index = i; 1573 } else { 1574 location->index = i + 1; 1575 } 1576 break; 1577 } 1578 } 1579 1580 if (!found) { 1581 if (overflow_button_->visible()) { 1582 // Are we over the overflow button? 1583 int overflow_delta_x = mirrored_x - overflow_button_->x(); 1584 if (overflow_delta_x >= 0 && 1585 overflow_delta_x < overflow_button_->width()) { 1586 // Mouse is over overflow button. 1587 location->index = GetFirstHiddenNodeIndex(); 1588 location->button_type = DROP_OVERFLOW; 1589 } else if (overflow_delta_x < 0) { 1590 // Mouse is after the last visible button but before overflow button; 1591 // use the last visible index. 1592 location->index = GetFirstHiddenNodeIndex(); 1593 } else { 1594 return; 1595 } 1596 } else if (!other_bookmarked_button_->visible() || 1597 mirrored_x < other_bookmarked_button_->x()) { 1598 // Mouse is after the last visible button but before more recently 1599 // bookmarked; use the last visible index. 1600 location->index = GetFirstHiddenNodeIndex(); 1601 } else { 1602 return; 1603 } 1604 } 1605 1606 if (location->on) { 1607 const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ? 1608 model_->other_node() : 1609 model_->bookmark_bar_node()->GetChild(location->index); 1610 location->operation = chrome::GetBookmarkDropOperation( 1611 profile, event, data, parent, parent->child_count()); 1612 if (!location->operation && !data.has_single_url() && 1613 data.GetFirstNode(profile) == parent) { 1614 // Don't open a menu if the node being dragged is the menu to open. 1615 location->on = false; 1616 } 1617 } else { 1618 location->operation = chrome::GetBookmarkDropOperation(profile, event, 1619 data, model_->bookmark_bar_node(), location->index); 1620 } 1621 } 1622 1623 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node, 1624 ui::OSExchangeData* data) { 1625 DCHECK(node && data); 1626 BookmarkNodeData drag_data(node); 1627 drag_data.Write(browser_->profile(), data); 1628 } 1629 1630 void BookmarkBarView::StartThrobbing(const BookmarkNode* node, 1631 bool overflow_only) { 1632 DCHECK(!throbbing_view_); 1633 1634 // Determine which visible button is showing the bookmark (or is an ancestor 1635 // of the bookmark). 1636 const BookmarkNode* bbn = model_->bookmark_bar_node(); 1637 const BookmarkNode* parent_on_bb = node; 1638 while (parent_on_bb) { 1639 const BookmarkNode* parent = parent_on_bb->parent(); 1640 if (parent == bbn) 1641 break; 1642 parent_on_bb = parent; 1643 } 1644 if (parent_on_bb) { 1645 int index = bbn->GetIndexOf(parent_on_bb); 1646 if (index >= GetFirstHiddenNodeIndex()) { 1647 // Node is hidden, animate the overflow button. 1648 throbbing_view_ = overflow_button_; 1649 } else if (!overflow_only) { 1650 throbbing_view_ = static_cast<CustomButton*>(child_at(index)); 1651 } 1652 } else if (!overflow_only) { 1653 throbbing_view_ = other_bookmarked_button_; 1654 } 1655 1656 // Use a large number so that the button continues to throb. 1657 if (throbbing_view_) 1658 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max()); 1659 } 1660 1661 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove( 1662 const BookmarkNode* parent, 1663 int old_index) { 1664 const BookmarkNode* bbn = model_->bookmark_bar_node(); 1665 const BookmarkNode* old_node = parent; 1666 int old_index_on_bb = old_index; 1667 while (old_node && old_node != bbn) { 1668 const BookmarkNode* parent = old_node->parent(); 1669 if (parent == bbn) { 1670 old_index_on_bb = bbn->GetIndexOf(old_node); 1671 break; 1672 } 1673 old_node = parent; 1674 } 1675 if (old_node) { 1676 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) { 1677 // Node is hidden, animate the overflow button. 1678 return overflow_button_; 1679 } 1680 return static_cast<CustomButton*>(child_at(old_index_on_bb)); 1681 } 1682 // Node wasn't on the bookmark bar, use the other bookmark button. 1683 return other_bookmarked_button_; 1684 } 1685 1686 void BookmarkBarView::UpdateColors() { 1687 // We don't always have a theme provider (ui tests, for example). 1688 const ui::ThemeProvider* theme_provider = GetThemeProvider(); 1689 if (!theme_provider) 1690 return; 1691 SkColor text_color = 1692 theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT); 1693 for (int i = 0; i < GetBookmarkButtonCount(); ++i) 1694 GetBookmarkButton(i)->SetEnabledColor(text_color); 1695 other_bookmarked_button()->SetEnabledColor(text_color); 1696 if (apps_page_shortcut_->visible()) 1697 apps_page_shortcut_->SetEnabledColor(text_color); 1698 } 1699 1700 void BookmarkBarView::UpdateOtherBookmarksVisibility() { 1701 bool has_other_children = !model_->other_node()->empty(); 1702 if (has_other_children == other_bookmarked_button_->visible()) 1703 return; 1704 other_bookmarked_button_->SetVisible(has_other_children); 1705 UpdateBookmarksSeparatorVisibility(); 1706 Layout(); 1707 SchedulePaint(); 1708 } 1709 1710 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() { 1711 // Ash does not paint the bookmarks separator line because it looks odd on 1712 // the flat background. We keep it present for layout, but don't draw it. 1713 bookmarks_separator_view_->SetVisible( 1714 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH && 1715 other_bookmarked_button_->visible()); 1716 } 1717 1718 gfx::Size BookmarkBarView::LayoutItems(bool compute_bounds_only) { 1719 gfx::Size prefsize; 1720 if (!parent() && !compute_bounds_only) 1721 return prefsize; 1722 1723 int x = kLeftMargin; 1724 int top_margin = IsDetached() ? kDetachedTopMargin : 0; 1725 int y = top_margin; 1726 int width = View::width() - kRightMargin - kLeftMargin; 1727 int height = browser_defaults::kBookmarkBarHeight - kBottomMargin; 1728 int separator_margin = kSeparatorMargin; 1729 1730 if (IsDetached()) { 1731 double current_state = 1 - size_animation_->GetCurrentValue(); 1732 x += static_cast<int>(kNewtabHorizontalPadding * current_state); 1733 y += (View::height() - browser_defaults::kBookmarkBarHeight) / 2; 1734 width -= static_cast<int>(kNewtabHorizontalPadding * current_state); 1735 separator_margin -= static_cast<int>(kSeparatorMargin * current_state); 1736 } else { 1737 // For the attached appearance, pin the content to the bottom of the bar 1738 // when animating in/out, as shrinking its height instead looks weird. This 1739 // also matches how we layout infobars. 1740 y += View::height() - browser_defaults::kBookmarkBarHeight; 1741 } 1742 1743 gfx::Size other_bookmarked_pref = other_bookmarked_button_->visible() ? 1744 other_bookmarked_button_->GetPreferredSize() : gfx::Size(); 1745 gfx::Size overflow_pref = overflow_button_->GetPreferredSize(); 1746 gfx::Size bookmarks_separator_pref = 1747 bookmarks_separator_view_->GetPreferredSize(); 1748 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ? 1749 apps_page_shortcut_->GetPreferredSize() : gfx::Size(); 1750 1751 int max_x = width - overflow_pref.width() - kButtonPadding - 1752 bookmarks_separator_pref.width(); 1753 if (other_bookmarked_button_->visible()) 1754 max_x -= other_bookmarked_pref.width() + kButtonPadding; 1755 1756 // Next, layout out the buttons. Any buttons that are placed beyond the 1757 // visible region and made invisible. 1758 1759 // Start with the apps page shortcut button. 1760 if (apps_page_shortcut_->visible()) { 1761 if (!compute_bounds_only) { 1762 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(), 1763 height); 1764 } 1765 x += apps_page_shortcut_pref.width() + kButtonPadding; 1766 } 1767 1768 // Then go through the bookmark buttons. 1769 if (GetBookmarkButtonCount() == 0 && model_ && model_->loaded()) { 1770 gfx::Size pref = instructions_->GetPreferredSize(); 1771 if (!compute_bounds_only) { 1772 instructions_->SetBounds( 1773 x + kInstructionsPadding, y, 1774 std::min(static_cast<int>(pref.width()), 1775 max_x - x), 1776 height); 1777 instructions_->SetVisible(true); 1778 } 1779 } else { 1780 if (!compute_bounds_only) 1781 instructions_->SetVisible(false); 1782 1783 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1784 views::View* child = child_at(i); 1785 gfx::Size pref = child->GetPreferredSize(); 1786 int next_x = x + pref.width() + kButtonPadding; 1787 if (!compute_bounds_only) { 1788 child->SetVisible(next_x < max_x); 1789 child->SetBounds(x, y, pref.width(), height); 1790 } 1791 x = next_x; 1792 } 1793 } 1794 1795 // Layout the right side of the bar. 1796 const bool all_visible = (GetBookmarkButtonCount() == 0 || 1797 child_at(GetBookmarkButtonCount() - 1)->visible()); 1798 1799 // Layout the right side buttons. 1800 if (!compute_bounds_only) 1801 x = max_x + kButtonPadding; 1802 else 1803 x += kButtonPadding; 1804 1805 // The overflow button. 1806 if (!compute_bounds_only) { 1807 overflow_button_->SetBounds(x, y, overflow_pref.width(), height); 1808 overflow_button_->SetVisible(!all_visible); 1809 } 1810 x += overflow_pref.width(); 1811 1812 // Separator. 1813 if (bookmarks_separator_view_->visible()) { 1814 if (!compute_bounds_only) { 1815 bookmarks_separator_view_->SetBounds(x, 1816 y - top_margin, 1817 bookmarks_separator_pref.width(), 1818 height + top_margin + kBottomMargin - 1819 separator_margin); 1820 } 1821 1822 x += bookmarks_separator_pref.width(); 1823 } 1824 1825 // The other bookmarks button. 1826 if (other_bookmarked_button_->visible()) { 1827 if (!compute_bounds_only) { 1828 other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.width(), 1829 height); 1830 } 1831 x += other_bookmarked_pref.width() + kButtonPadding; 1832 } 1833 1834 // Set the preferred size computed so far. 1835 if (compute_bounds_only) { 1836 x += kRightMargin; 1837 prefsize.set_width(x); 1838 if (IsDetached()) { 1839 x += static_cast<int>(kNewtabHorizontalPadding * 1840 (1 - size_animation_->GetCurrentValue())); 1841 prefsize.set_height( 1842 browser_defaults::kBookmarkBarHeight + 1843 static_cast<int>( 1844 (chrome::kNTPBookmarkBarHeight - 1845 browser_defaults::kBookmarkBarHeight) * 1846 (1 - size_animation_->GetCurrentValue()))); 1847 } else { 1848 prefsize.set_height( 1849 static_cast<int>( 1850 browser_defaults::kBookmarkBarHeight * 1851 size_animation_->GetCurrentValue())); 1852 } 1853 } 1854 return prefsize; 1855 } 1856 1857 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() { 1858 DCHECK(apps_page_shortcut_); 1859 // Only perform layout if required. 1860 bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar( 1861 browser_->profile()); 1862 if (apps_page_shortcut_->visible() == visible) 1863 return; 1864 apps_page_shortcut_->SetVisible(visible); 1865 UpdateBookmarksSeparatorVisibility(); 1866 Layout(); 1867 } 1868