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