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