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