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 "content/public/browser/notification_details.h" 54 #include "content/public/browser/notification_source.h" 55 #include "content/public/browser/page_navigator.h" 56 #include "content/public/browser/render_view_host.h" 57 #include "content/public/browser/render_widget_host_view.h" 58 #include "content/public/browser/user_metrics.h" 59 #include "content/public/browser/web_contents.h" 60 #include "content/public/common/page_transition_types.h" 61 #include "extensions/browser/extension_registry.h" 62 #include "extensions/common/extension.h" 63 #include "extensions/common/extension_set.h" 64 #include "grit/generated_resources.h" 65 #include "grit/theme_resources.h" 66 #include "grit/ui_resources.h" 67 #include "ui/accessibility/ax_view_state.h" 68 #include "ui/base/dragdrop/drag_utils.h" 69 #include "ui/base/dragdrop/os_exchange_data.h" 70 #include "ui/base/l10n/l10n_util.h" 71 #include "ui/base/resource/resource_bundle.h" 72 #include "ui/base/theme_provider.h" 73 #include "ui/base/window_open_disposition.h" 74 #include "ui/gfx/animation/slide_animation.h" 75 #include "ui/gfx/canvas.h" 76 #include "ui/gfx/text_constants.h" 77 #include "ui/gfx/text_elider.h" 78 #include "ui/views/button_drag_utils.h" 79 #include "ui/views/controls/button/label_button.h" 80 #include "ui/views/controls/button/label_button_border.h" 81 #include "ui/views/controls/button/menu_button.h" 82 #include "ui/views/controls/label.h" 83 #include "ui/views/drag_utils.h" 84 #include "ui/views/metrics.h" 85 #include "ui/views/view_constants.h" 86 #include "ui/views/widget/tooltip_manager.h" 87 #include "ui/views/widget/widget.h" 88 #include "ui/views/window/non_client_view.h" 89 90 using base::UserMetricsAction; 91 using content::OpenURLParams; 92 using content::PageNavigator; 93 using content::Referrer; 94 using ui::DropTargetEvent; 95 using views::CustomButton; 96 using views::LabelButtonBorder; 97 using views::MenuButton; 98 using views::View; 99 100 // Margins around the content. 101 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the 102 // toolbar above serve as the margin. 103 static const int kBottomMargin = 2; 104 static const int kLeftMargin = 1; 105 static const int kRightMargin = 1; 106 107 // static 108 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView"; 109 110 // Padding between buttons. 111 static const int kButtonPadding = 0; 112 113 // Icon to display when one isn't found for the page. 114 static gfx::ImageSkia* kDefaultFavicon = NULL; 115 116 // Icon used for folders. 117 static gfx::ImageSkia* kFolderIcon = NULL; 118 119 // Color of the drop indicator. 120 static const SkColor kDropIndicatorColor = SK_ColorBLACK; 121 122 // Width of the drop indicator. 123 static const int kDropIndicatorWidth = 2; 124 125 // Distance between the bottom of the bar and the separator. 126 static const int kSeparatorMargin = 1; 127 128 // Width of the separator between the recently bookmarked button and the 129 // overflow indicator. 130 static const int kSeparatorWidth = 4; 131 132 // Starting x-coordinate of the separator line within a separator. 133 static const int kSeparatorStartX = 2; 134 135 // Left-padding for the instructional text. 136 static const int kInstructionsPadding = 6; 137 138 // Tag for the 'Other bookmarks' button. 139 static const int kOtherFolderButtonTag = 1; 140 141 // Tag for the 'Apps Shortcut' button. 142 static const int kAppsShortcutButtonTag = 2; 143 144 // Preferred padding between text and edge. 145 // 146 // Note that the vertical padding is one pixel less than it was in TextButton; 147 // we clip the bottom of letters like 'g' or 'p' if we don't. 148 static const int kButtonPaddingHorizontal = 6; 149 static const int kButtonPaddingVertical = 4; 150 151 // Tag for the 'Managed bookmarks' button. 152 static const int kManagedFolderButtonTag = 3; 153 154 #if !defined(OS_WIN) 155 static const gfx::ElideBehavior kElideBehavior = gfx::FADE_TAIL; 156 #else 157 // Windows fade eliding causes text to darken; see http://crbug.com/388084 158 static const gfx::ElideBehavior kElideBehavior = gfx::ELIDE_TAIL; 159 #endif 160 161 namespace { 162 163 // To enable/disable BookmarkBar animations during testing. In production 164 // animations are enabled by default. 165 bool animations_enabled = true; 166 167 // BookmarkButtonBase ----------------------------------------------- 168 169 // Base class for text buttons used on the bookmark bar. 170 171 class BookmarkButtonBase : public views::LabelButton { 172 public: 173 BookmarkButtonBase(views::ButtonListener* listener, 174 const base::string16& title) 175 : LabelButton(listener, title) { 176 SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); 177 SetElideBehavior(kElideBehavior); 178 show_animation_.reset(new gfx::SlideAnimation(this)); 179 if (!animations_enabled) { 180 // For some reason during testing the events generated by animating 181 // throw off the test. So, don't animate while testing. 182 show_animation_->Reset(1); 183 } else { 184 show_animation_->Show(); 185 } 186 } 187 188 virtual bool IsTriggerableEvent(const ui::Event& e) OVERRIDE { 189 return e.type() == ui::ET_GESTURE_TAP || 190 e.type() == ui::ET_GESTURE_TAP_DOWN || 191 event_utils::IsPossibleDispositionEvent(e); 192 } 193 194 virtual scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const OVERRIDE { 195 // We change the insets on the border to match the previous TextButton. 196 scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder(); 197 border->set_insets(gfx::Insets(kButtonPaddingVertical, 198 kButtonPaddingHorizontal, 199 kButtonPaddingVertical, 200 kButtonPaddingHorizontal)); 201 return border.Pass(); 202 } 203 204 private: 205 scoped_ptr<gfx::SlideAnimation> show_animation_; 206 207 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase); 208 }; 209 210 // BookmarkButton ------------------------------------------------------------- 211 212 // Buttons used for the bookmarks on the bookmark bar. 213 214 class BookmarkButton : public BookmarkButtonBase { 215 public: 216 // The internal view class name. 217 static const char kViewClassName[]; 218 219 BookmarkButton(views::ButtonListener* listener, 220 const GURL& url, 221 const base::string16& title, 222 Profile* profile) 223 : BookmarkButtonBase(listener, title), 224 url_(url), 225 profile_(profile) { 226 } 227 228 virtual bool GetTooltipText(const gfx::Point& p, 229 base::string16* tooltip) const OVERRIDE { 230 gfx::Point location(p); 231 ConvertPointToScreen(this, &location); 232 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle( 233 GetWidget(), location, url_, GetText(), profile_); 234 return !tooltip->empty(); 235 } 236 237 virtual const char* GetClassName() const OVERRIDE { 238 return kViewClassName; 239 } 240 241 private: 242 const GURL& url_; 243 Profile* profile_; 244 245 DISALLOW_COPY_AND_ASSIGN(BookmarkButton); 246 }; 247 248 // static 249 const char BookmarkButton::kViewClassName[] = "BookmarkButton"; 250 251 // ShortcutButton ------------------------------------------------------------- 252 253 // Buttons used for the shortcuts on the bookmark bar. 254 255 class ShortcutButton : public BookmarkButtonBase { 256 public: 257 // The internal view class name. 258 static const char kViewClassName[]; 259 260 ShortcutButton(views::ButtonListener* listener, 261 const base::string16& title) 262 : BookmarkButtonBase(listener, title) { 263 } 264 265 virtual const char* GetClassName() const OVERRIDE { 266 return kViewClassName; 267 } 268 269 private: 270 271 DISALLOW_COPY_AND_ASSIGN(ShortcutButton); 272 }; 273 274 // static 275 const char ShortcutButton::kViewClassName[] = "ShortcutButton"; 276 277 // BookmarkFolderButton ------------------------------------------------------- 278 279 // Buttons used for folders on the bookmark bar, including the 'other folders' 280 // button. 281 class BookmarkFolderButton : public views::MenuButton { 282 public: 283 BookmarkFolderButton(views::ButtonListener* listener, 284 const base::string16& title, 285 views::MenuButtonListener* menu_button_listener, 286 bool show_menu_marker) 287 : MenuButton(listener, title, menu_button_listener, show_menu_marker) { 288 SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); 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 329 class 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 346 void 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 362 struct 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 392 struct 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 418 class 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 449 const int BookmarkBarView::kMaxButtonWidth = 150; 450 const int BookmarkBarView::kNewtabHorizontalPadding = 2; 451 const int BookmarkBarView::kToolbarAttachedBookmarkBarOverlap = 3; 452 453 const 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 461 const 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 469 BookmarkBarView::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 493 BookmarkBarView::~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 511 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) { 512 animations_enabled = !disabled; 513 } 514 515 void 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 523 void 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 540 int 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 549 bool BookmarkBarView::is_animating() { 550 return size_animation_->is_animating(); 551 } 552 553 const 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 594 views::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 608 void 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 617 views::MenuItemView* BookmarkBarView::GetMenu() { 618 return bookmark_menu_ ? bookmark_menu_->menu() : NULL; 619 } 620 621 views::MenuItemView* BookmarkBarView::GetContextMenu() { 622 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL; 623 } 624 625 views::MenuItemView* BookmarkBarView::GetDropMenu() { 626 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL; 627 } 628 629 void 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 639 base::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 680 bool BookmarkBarView::IsDetached() const { 681 return (bookmark_bar_state_ == BookmarkBar::DETACHED) || 682 (animating_detached_ && size_animation_->is_animating()); 683 } 684 685 double BookmarkBarView::GetAnimationValue() const { 686 return size_animation_->GetCurrentValue(); 687 } 688 689 int 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 709 gfx::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 724 bool 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 737 gfx::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 777 void BookmarkBarView::Layout() { 778 LayoutItems(); 779 } 780 781 void 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 798 void 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 839 bool 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 849 bool BookmarkBarView::AreDropTypesRequired() { 850 return true; 851 } 852 853 bool 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 867 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) { 868 } 869 870 int 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 922 void 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 937 int 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 973 void BookmarkBarView::OnThemeChanged() { 974 UpdateColors(); 975 } 976 977 const char* BookmarkBarView::GetClassName() const { 978 return kViewClassName; 979 } 980 981 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) { 982 state->role = ui::AX_ROLE_TOOLBAR; 983 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS); 984 } 985 986 void 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 992 void 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 1000 void 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 1008 void BookmarkBarView::ShowImportDialog() { 1009 int64 install_time = 1010 g_browser_process->local_state()->GetInt64(prefs::kInstallDate); 1011 int64 time_from_install = base::Time::Now().ToTimeT() - install_time; 1012 if (bookmark_bar_state_ == BookmarkBar::SHOW) { 1013 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView", 1014 time_from_install); 1015 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) { 1016 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView", 1017 time_from_install); 1018 } 1019 1020 chrome::ShowImportDialog(browser_); 1021 } 1022 1023 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) { 1024 StopThrobbing(true); 1025 const BookmarkNode* node = model_->GetMostRecentlyAddedUserNodeForURL(url); 1026 if (!node) 1027 return; // Generally shouldn't happen. 1028 StartThrobbing(node, false); 1029 } 1030 1031 void BookmarkBarView::OnBookmarkBubbleHidden() { 1032 StopThrobbing(false); 1033 } 1034 1035 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model, 1036 bool ids_reassigned) { 1037 // There should be no buttons. If non-zero it means Load was invoked more than 1038 // once, or we didn't properly clear things. Either of which shouldn't happen. 1039 DCHECK_EQ(0, GetBookmarkButtonCount()); 1040 const BookmarkNode* node = model->bookmark_bar_node(); 1041 DCHECK(node); 1042 // Create a button for each of the children on the bookmark bar. 1043 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) 1044 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i); 1045 DCHECK(model->other_node()); 1046 other_bookmarked_button_->SetAccessibleName(model->other_node()->GetTitle()); 1047 other_bookmarked_button_->SetText(model->other_node()->GetTitle()); 1048 managed_bookmarks_button_->SetAccessibleName( 1049 client_->managed_node()->GetTitle()); 1050 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle()); 1051 UpdateColors(); 1052 UpdateButtonsVisibility(); 1053 other_bookmarked_button_->SetEnabled(true); 1054 managed_bookmarks_button_->SetEnabled(true); 1055 1056 Layout(); 1057 SchedulePaint(); 1058 } 1059 1060 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) { 1061 NOTREACHED(); 1062 // Do minimal cleanup, presumably we'll be deleted shortly. 1063 model_->RemoveObserver(this); 1064 model_ = NULL; 1065 } 1066 1067 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model, 1068 const BookmarkNode* old_parent, 1069 int old_index, 1070 const BookmarkNode* new_parent, 1071 int new_index) { 1072 bool was_throbbing = throbbing_view_ && 1073 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index); 1074 if (was_throbbing) 1075 throbbing_view_->StopThrobbing(); 1076 BookmarkNodeRemovedImpl(model, old_parent, old_index); 1077 BookmarkNodeAddedImpl(model, new_parent, new_index); 1078 if (was_throbbing) 1079 StartThrobbing(new_parent->GetChild(new_index), false); 1080 } 1081 1082 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model, 1083 const BookmarkNode* parent, 1084 int index) { 1085 BookmarkNodeAddedImpl(model, parent, index); 1086 } 1087 1088 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model, 1089 const BookmarkNode* parent, 1090 int old_index, 1091 const BookmarkNode* node, 1092 const std::set<GURL>& removed_urls) { 1093 // Close the menu if the menu is showing for the deleted node. 1094 if (bookmark_menu_ && bookmark_menu_->node() == node) 1095 bookmark_menu_->Cancel(); 1096 BookmarkNodeRemovedImpl(model, parent, old_index); 1097 } 1098 1099 void BookmarkBarView::BookmarkAllUserNodesRemoved( 1100 BookmarkModel* model, 1101 const std::set<GURL>& removed_urls) { 1102 UpdateButtonsVisibility(); 1103 1104 StopThrobbing(true); 1105 1106 // Remove the existing buttons. 1107 while (GetBookmarkButtonCount()) { 1108 delete GetBookmarkButton(0); 1109 } 1110 1111 Layout(); 1112 SchedulePaint(); 1113 } 1114 1115 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model, 1116 const BookmarkNode* node) { 1117 BookmarkNodeChangedImpl(model, node); 1118 } 1119 1120 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model, 1121 const BookmarkNode* node) { 1122 if (node != model->bookmark_bar_node()) 1123 return; // We only care about reordering of the bookmark bar node. 1124 1125 // Remove the existing buttons. 1126 while (GetBookmarkButtonCount()) { 1127 views::View* button = child_at(0); 1128 RemoveChildView(button); 1129 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button); 1130 } 1131 1132 // Create the new buttons. 1133 for (int i = 0, child_count = node->child_count(); i < child_count; ++i) 1134 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i); 1135 UpdateColors(); 1136 1137 Layout(); 1138 SchedulePaint(); 1139 } 1140 1141 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model, 1142 const BookmarkNode* node) { 1143 BookmarkNodeChangedImpl(model, node); 1144 } 1145 1146 void BookmarkBarView::WriteDragDataForView(View* sender, 1147 const gfx::Point& press_pt, 1148 ui::OSExchangeData* data) { 1149 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton")); 1150 1151 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1152 if (sender == GetBookmarkButton(i)) { 1153 views::LabelButton* button = GetBookmarkButton(i); 1154 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i); 1155 1156 const gfx::Image& image_from_model = model_->GetFavicon(node); 1157 const gfx::ImageSkia& icon = image_from_model.IsEmpty() ? 1158 (node->is_folder() ? GetFolderIcon() : GetDefaultFavicon()) : 1159 *image_from_model.ToImageSkia(); 1160 1161 button_drag_utils::SetDragImage( 1162 node->url(), 1163 node->GetTitle(), 1164 icon, 1165 &press_pt, 1166 data, 1167 button->GetWidget()); 1168 WriteBookmarkDragData(model_->bookmark_bar_node()->GetChild(i), data); 1169 return; 1170 } 1171 } 1172 NOTREACHED(); 1173 } 1174 1175 int BookmarkBarView::GetDragOperationsForView(View* sender, 1176 const gfx::Point& p) { 1177 if (size_animation_->is_animating() || 1178 (size_animation_->GetCurrentValue() == 0 && 1179 bookmark_bar_state_ != BookmarkBar::DETACHED)) { 1180 // Don't let the user drag while animating open or we're closed (and not 1181 // detached, when detached size_animation_ is always 0). This typically is 1182 // only hit if the user does something to inadvertently trigger DnD such as 1183 // pressing the mouse and hitting control-b. 1184 return ui::DragDropTypes::DRAG_NONE; 1185 } 1186 1187 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1188 if (sender == GetBookmarkButton(i)) { 1189 return chrome::GetBookmarkDragOperation( 1190 browser_->profile(), model_->bookmark_bar_node()->GetChild(i)); 1191 } 1192 } 1193 NOTREACHED(); 1194 return ui::DragDropTypes::DRAG_NONE; 1195 } 1196 1197 bool BookmarkBarView::CanStartDragForView(views::View* sender, 1198 const gfx::Point& press_pt, 1199 const gfx::Point& p) { 1200 // Check if we have not moved enough horizontally but we have moved downward 1201 // vertically - downward drag. 1202 gfx::Vector2d move_offset = p - press_pt; 1203 gfx::Vector2d horizontal_offset(move_offset.x(), 0); 1204 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) { 1205 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1206 if (sender == GetBookmarkButton(i)) { 1207 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i); 1208 // If the folder button was dragged, show the menu instead. 1209 if (node && node->is_folder()) { 1210 views::MenuButton* menu_button = 1211 static_cast<views::MenuButton*>(sender); 1212 menu_button->Activate(); 1213 return false; 1214 } 1215 break; 1216 } 1217 } 1218 } 1219 return true; 1220 } 1221 1222 void BookmarkBarView::OnMenuButtonClicked(views::View* view, 1223 const gfx::Point& point) { 1224 const BookmarkNode* node; 1225 1226 int start_index = 0; 1227 if (view == other_bookmarked_button_) { 1228 node = model_->other_node(); 1229 } else if (view == managed_bookmarks_button_) { 1230 node = client_->managed_node(); 1231 } else if (view == overflow_button_) { 1232 node = model_->bookmark_bar_node(); 1233 start_index = GetFirstHiddenNodeIndex(); 1234 } else { 1235 int button_index = GetIndexOf(view); 1236 DCHECK_NE(-1, button_index); 1237 node = model_->bookmark_bar_node()->GetChild(button_index); 1238 } 1239 1240 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation()); 1241 bookmark_menu_ = new BookmarkMenuController( 1242 browser_, page_navigator_, GetWidget(), node, start_index); 1243 bookmark_menu_->set_observer(this); 1244 bookmark_menu_->RunMenuAt(this, false); 1245 } 1246 1247 void BookmarkBarView::ButtonPressed(views::Button* sender, 1248 const ui::Event& event) { 1249 WindowOpenDisposition disposition_from_event_flags = 1250 ui::DispositionFromEventFlags(event.flags()); 1251 1252 if (sender->tag() == kAppsShortcutButtonTag) { 1253 OpenURLParams params(GURL(chrome::kChromeUIAppsURL), 1254 Referrer(), 1255 disposition_from_event_flags, 1256 content::PAGE_TRANSITION_AUTO_BOOKMARK, 1257 false); 1258 page_navigator_->OpenURL(params); 1259 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation()); 1260 return; 1261 } 1262 1263 const BookmarkNode* node; 1264 if (sender->tag() == kOtherFolderButtonTag) { 1265 node = model_->other_node(); 1266 } else if (sender->tag() == kManagedFolderButtonTag) { 1267 node = client_->managed_node(); 1268 } else { 1269 int index = GetIndexOf(sender); 1270 DCHECK_NE(-1, index); 1271 node = model_->bookmark_bar_node()->GetChild(index); 1272 } 1273 DCHECK(page_navigator_); 1274 1275 if (node->is_url()) { 1276 RecordAppLaunch(browser_->profile(), node->url()); 1277 OpenURLParams params( 1278 node->url(), Referrer(), disposition_from_event_flags, 1279 content::PAGE_TRANSITION_AUTO_BOOKMARK, false); 1280 page_navigator_->OpenURL(params); 1281 } else { 1282 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node, 1283 disposition_from_event_flags, browser_->profile()); 1284 } 1285 1286 RecordBookmarkLaunch(node, GetBookmarkLaunchLocation()); 1287 } 1288 1289 void BookmarkBarView::ShowContextMenuForView(views::View* source, 1290 const gfx::Point& point, 1291 ui::MenuSourceType source_type) { 1292 if (!model_->loaded()) { 1293 // Don't do anything if the model isn't loaded. 1294 return; 1295 } 1296 1297 const BookmarkNode* parent = NULL; 1298 std::vector<const BookmarkNode*> nodes; 1299 if (source == other_bookmarked_button_) { 1300 parent = model_->other_node(); 1301 // Do this so the user can open all bookmarks. BookmarkContextMenu makes 1302 // sure the user can't edit/delete the node in this case. 1303 nodes.push_back(parent); 1304 } else if (source == managed_bookmarks_button_) { 1305 parent = client_->managed_node(); 1306 nodes.push_back(parent); 1307 } else if (source != this && source != apps_page_shortcut_) { 1308 // User clicked on one of the bookmark buttons, find which one they 1309 // clicked on, except for the apps page shortcut, which must behave as if 1310 // the user clicked on the bookmark bar background. 1311 int bookmark_button_index = GetIndexOf(source); 1312 DCHECK(bookmark_button_index != -1 && 1313 bookmark_button_index < GetBookmarkButtonCount()); 1314 const BookmarkNode* node = 1315 model_->bookmark_bar_node()->GetChild(bookmark_button_index); 1316 nodes.push_back(node); 1317 parent = node->parent(); 1318 } else { 1319 parent = model_->bookmark_bar_node(); 1320 nodes.push_back(parent); 1321 } 1322 bool close_on_remove = 1323 (parent == model_->other_node()) && (parent->child_count() == 1); 1324 1325 context_menu_.reset(new BookmarkContextMenu( 1326 GetWidget(), browser_, browser_->profile(), 1327 browser_->tab_strip_model()->GetActiveWebContents(), 1328 parent, nodes, close_on_remove)); 1329 context_menu_->RunMenuAt(point, source_type); 1330 } 1331 1332 void BookmarkBarView::Init() { 1333 // Note that at this point we're not in a hierarchy so GetThemeProvider() will 1334 // return NULL. When we're inserted into a hierarchy, we'll call 1335 // UpdateColors(), which will set the appropriate colors for all the objects 1336 // added in this function. 1337 1338 // Child views are traversed in the order they are added. Make sure the order 1339 // they are added matches the visual order. 1340 overflow_button_ = CreateOverflowButton(); 1341 AddChildView(overflow_button_); 1342 1343 other_bookmarked_button_ = CreateOtherBookmarkedButton(); 1344 // We'll re-enable when the model is loaded. 1345 other_bookmarked_button_->SetEnabled(false); 1346 AddChildView(other_bookmarked_button_); 1347 1348 managed_bookmarks_button_ = CreateManagedBookmarksButton(); 1349 // Also re-enabled when the model is loaded. 1350 managed_bookmarks_button_->SetEnabled(false); 1351 AddChildView(managed_bookmarks_button_); 1352 1353 apps_page_shortcut_ = CreateAppsPageShortcutButton(); 1354 AddChildView(apps_page_shortcut_); 1355 profile_pref_registrar_.Init(browser_->profile()->GetPrefs()); 1356 profile_pref_registrar_.Add( 1357 prefs::kShowAppsShortcutInBookmarkBar, 1358 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged, 1359 base::Unretained(this))); 1360 profile_pref_registrar_.Add( 1361 prefs::kShowManagedBookmarksInBookmarkBar, 1362 base::Bind(&BookmarkBarView::UpdateButtonsVisibility, 1363 base::Unretained(this))); 1364 apps_page_shortcut_->SetVisible( 1365 chrome::ShouldShowAppsShortcutInBookmarkBar( 1366 browser_->profile(), browser_->host_desktop_type())); 1367 1368 bookmarks_separator_view_ = new ButtonSeparatorView(); 1369 AddChildView(bookmarks_separator_view_); 1370 UpdateBookmarksSeparatorVisibility(); 1371 1372 instructions_ = new BookmarkBarInstructionsView(this); 1373 AddChildView(instructions_); 1374 1375 set_context_menu_controller(this); 1376 1377 size_animation_.reset(new gfx::SlideAnimation(this)); 1378 1379 model_ = BookmarkModelFactory::GetForProfile(browser_->profile()); 1380 client_ = ChromeBookmarkClientFactory::GetForProfile(browser_->profile()); 1381 if (model_) { 1382 model_->AddObserver(this); 1383 if (model_->loaded()) 1384 BookmarkModelLoaded(model_, false); 1385 // else case: we'll receive notification back from the BookmarkModel when 1386 // done loading, then we'll populate the bar. 1387 } 1388 } 1389 1390 int BookmarkBarView::GetBookmarkButtonCount() const { 1391 // We contain six non-bookmark button views: managed bookmarks, 1392 // other bookmarks, bookmarks separator, chevrons (for overflow), apps page, 1393 // and the instruction label. 1394 return child_count() - 6; 1395 } 1396 1397 views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) { 1398 DCHECK(index >= 0 && index < GetBookmarkButtonCount()); 1399 return static_cast<views::LabelButton*>(child_at(index)); 1400 } 1401 1402 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const { 1403 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR : 1404 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR; 1405 } 1406 1407 int BookmarkBarView::GetFirstHiddenNodeIndex() { 1408 const int bb_count = GetBookmarkButtonCount(); 1409 for (int i = 0; i < bb_count; ++i) { 1410 if (!GetBookmarkButton(i)->visible()) 1411 return i; 1412 } 1413 return bb_count; 1414 } 1415 1416 MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() { 1417 // Title is set in Loaded. 1418 MenuButton* button = 1419 new BookmarkFolderButton(this, base::string16(), this, false); 1420 button->set_id(VIEW_ID_OTHER_BOOKMARKS); 1421 button->SetImage(views::Button::STATE_NORMAL, GetFolderIcon()); 1422 button->set_context_menu_controller(this); 1423 button->set_tag(kOtherFolderButtonTag); 1424 return button; 1425 } 1426 1427 MenuButton* BookmarkBarView::CreateManagedBookmarksButton() { 1428 // Title is set in Loaded. 1429 MenuButton* button = 1430 new BookmarkFolderButton(this, base::string16(), this, false); 1431 button->set_id(VIEW_ID_MANAGED_BOOKMARKS); 1432 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1433 gfx::ImageSkia* image = 1434 rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER_MANAGED); 1435 button->SetImage(views::Button::STATE_NORMAL, *image); 1436 button->set_context_menu_controller(this); 1437 button->set_tag(kManagedFolderButtonTag); 1438 return button; 1439 } 1440 1441 MenuButton* BookmarkBarView::CreateOverflowButton() { 1442 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1443 MenuButton* button = new OverFlowButton(this); 1444 button->SetImage(views::Button::STATE_NORMAL, 1445 *rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS)); 1446 1447 // The overflow button's image contains an arrow and therefore it is a 1448 // direction sensitive image and we need to flip it if the UI layout is 1449 // right-to-left. 1450 // 1451 // By default, menu buttons are not flipped because they generally contain 1452 // text and flipping the gfx::Canvas object will break text rendering. Since 1453 // the overflow button does not contain text, we can safely flip it. 1454 button->EnableCanvasFlippingForRTLUI(true); 1455 1456 // Make visible as necessary. 1457 button->SetVisible(false); 1458 // Set accessibility name. 1459 button->SetAccessibleName( 1460 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON)); 1461 return button; 1462 } 1463 1464 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) { 1465 if (node->is_url()) { 1466 BookmarkButton* button = new BookmarkButton( 1467 this, node->url(), node->GetTitle(), browser_->profile()); 1468 ConfigureButton(node, button); 1469 return button; 1470 } else { 1471 views::MenuButton* button = new BookmarkFolderButton( 1472 this, node->GetTitle(), this, false); 1473 button->SetImage(views::Button::STATE_NORMAL, GetFolderIcon()); 1474 ConfigureButton(node, button); 1475 return button; 1476 } 1477 } 1478 1479 views::LabelButton* BookmarkBarView::CreateAppsPageShortcutButton() { 1480 views::LabelButton* button = new ShortcutButton( 1481 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME)); 1482 button->SetTooltipText(l10n_util::GetStringUTF16( 1483 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP)); 1484 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT); 1485 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1486 button->SetImage(views::Button::STATE_NORMAL, 1487 *rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT)); 1488 button->set_context_menu_controller(this); 1489 button->set_tag(kAppsShortcutButtonTag); 1490 return button; 1491 } 1492 1493 void BookmarkBarView::ConfigureButton(const BookmarkNode* node, 1494 views::LabelButton* button) { 1495 button->SetText(node->GetTitle()); 1496 button->SetAccessibleName(node->GetTitle()); 1497 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT); 1498 // We don't always have a theme provider (ui tests, for example). 1499 if (GetThemeProvider()) { 1500 button->SetTextColor( 1501 views::Button::STATE_NORMAL, 1502 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)); 1503 } 1504 1505 button->set_min_size(gfx::Size()); 1506 button->set_context_menu_controller(this); 1507 button->set_drag_controller(this); 1508 if (node->is_url()) { 1509 const gfx::Image& favicon = model_->GetFavicon(node); 1510 if (!favicon.IsEmpty()) 1511 button->SetImage(views::Button::STATE_NORMAL, *favicon.ToImageSkia()); 1512 else 1513 button->SetImage(views::Button::STATE_NORMAL, GetDefaultFavicon()); 1514 } 1515 button->set_max_size(gfx::Size(kMaxButtonWidth, 0)); 1516 } 1517 1518 void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model, 1519 const BookmarkNode* parent, 1520 int index) { 1521 UpdateButtonsVisibility(); 1522 if (parent != model->bookmark_bar_node()) { 1523 // We only care about nodes on the bookmark bar. 1524 return; 1525 } 1526 DCHECK(index >= 0 && index <= GetBookmarkButtonCount()); 1527 const BookmarkNode* node = parent->GetChild(index); 1528 ProfileSyncService* sync_service(ProfileSyncServiceFactory:: 1529 GetInstance()->GetForProfile(browser_->profile())); 1530 if (!throbbing_view_ && sync_service && sync_service->FirstSetupInProgress()) 1531 StartThrobbing(node, true); 1532 AddChildViewAt(CreateBookmarkButton(node), index); 1533 UpdateColors(); 1534 Layout(); 1535 SchedulePaint(); 1536 } 1537 1538 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model, 1539 const BookmarkNode* parent, 1540 int index) { 1541 UpdateButtonsVisibility(); 1542 1543 StopThrobbing(true); 1544 // No need to start throbbing again as the bookmark bubble can't be up at 1545 // the same time as the user reorders. 1546 1547 if (parent != model->bookmark_bar_node()) { 1548 // We only care about nodes on the bookmark bar. 1549 return; 1550 } 1551 DCHECK(index >= 0 && index < GetBookmarkButtonCount()); 1552 views::View* button = child_at(index); 1553 RemoveChildView(button); 1554 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button); 1555 Layout(); 1556 SchedulePaint(); 1557 } 1558 1559 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model, 1560 const BookmarkNode* node) { 1561 if (node == client_->managed_node()) { 1562 // The managed node may have its title updated. 1563 managed_bookmarks_button_->SetAccessibleName( 1564 client_->managed_node()->GetTitle()); 1565 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle()); 1566 return; 1567 } 1568 1569 if (node->parent() != model->bookmark_bar_node()) { 1570 // We only care about nodes on the bookmark bar. 1571 return; 1572 } 1573 int index = model->bookmark_bar_node()->GetIndexOf(node); 1574 DCHECK_NE(-1, index); 1575 views::LabelButton* button = GetBookmarkButton(index); 1576 gfx::Size old_pref = button->GetPreferredSize(); 1577 ConfigureButton(node, button); 1578 gfx::Size new_pref = button->GetPreferredSize(); 1579 if (old_pref.width() != new_pref.width()) { 1580 Layout(); 1581 SchedulePaint(); 1582 } else if (button->visible()) { 1583 button->SchedulePaint(); 1584 } 1585 } 1586 1587 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) { 1588 if (bookmark_drop_menu_) { 1589 if (bookmark_drop_menu_->node() == node) { 1590 // Already showing for the specified node. 1591 return; 1592 } 1593 bookmark_drop_menu_->Cancel(); 1594 } 1595 1596 views::MenuButton* menu_button = GetMenuButtonForNode(node); 1597 if (!menu_button) 1598 return; 1599 1600 int start_index = 0; 1601 if (node == model_->bookmark_bar_node()) 1602 start_index = GetFirstHiddenNodeIndex(); 1603 1604 drop_info_->is_menu_showing = true; 1605 bookmark_drop_menu_ = new BookmarkMenuController(browser_, 1606 page_navigator_, GetWidget(), node, start_index); 1607 bookmark_drop_menu_->set_observer(this); 1608 bookmark_drop_menu_->RunMenuAt(this, true); 1609 } 1610 1611 void BookmarkBarView::StopShowFolderDropMenuTimer() { 1612 show_folder_method_factory_.InvalidateWeakPtrs(); 1613 } 1614 1615 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) { 1616 if (!animations_enabled) { 1617 // So that tests can run as fast as possible disable the delay during 1618 // testing. 1619 ShowDropFolderForNode(node); 1620 return; 1621 } 1622 show_folder_method_factory_.InvalidateWeakPtrs(); 1623 base::MessageLoop::current()->PostDelayedTask( 1624 FROM_HERE, 1625 base::Bind(&BookmarkBarView::ShowDropFolderForNode, 1626 show_folder_method_factory_.GetWeakPtr(), 1627 node), 1628 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay())); 1629 } 1630 1631 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event, 1632 const BookmarkNodeData& data, 1633 DropLocation* location) { 1634 DCHECK(model_); 1635 DCHECK(model_->loaded()); 1636 DCHECK(data.is_valid()); 1637 1638 *location = DropLocation(); 1639 1640 // The drop event uses the screen coordinates while the child Views are 1641 // always laid out from left to right (even though they are rendered from 1642 // right-to-left on RTL locales). Thus, in order to make sure the drop 1643 // coordinates calculation works, we mirror the event's X coordinate if the 1644 // locale is RTL. 1645 int mirrored_x = GetMirroredXInView(event.x()); 1646 1647 bool found = false; 1648 const int other_delta_x = mirrored_x - other_bookmarked_button_->x(); 1649 Profile* profile = browser_->profile(); 1650 if (other_bookmarked_button_->visible() && other_delta_x >= 0 && 1651 other_delta_x < other_bookmarked_button_->width()) { 1652 // Mouse is over 'other' folder. 1653 location->button_type = DROP_OTHER_FOLDER; 1654 location->on = true; 1655 found = true; 1656 } else if (!GetBookmarkButtonCount()) { 1657 // No bookmarks, accept the drop. 1658 location->index = 0; 1659 const BookmarkNode* node = data.GetFirstNode(model_, profile->GetPath()); 1660 int ops = node && client_->CanBeEditedByUser(node) ? 1661 ui::DragDropTypes::DRAG_MOVE : 1662 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK; 1663 location->operation = chrome::GetPreferredBookmarkDropOperation( 1664 event.source_operations(), ops); 1665 return; 1666 } 1667 1668 for (int i = 0; i < GetBookmarkButtonCount() && 1669 GetBookmarkButton(i)->visible() && !found; i++) { 1670 views::LabelButton* button = GetBookmarkButton(i); 1671 int button_x = mirrored_x - button->x(); 1672 int button_w = button->width(); 1673 if (button_x < button_w) { 1674 found = true; 1675 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i); 1676 if (node->is_folder()) { 1677 if (button_x <= views::kDropBetweenPixels) { 1678 location->index = i; 1679 } else if (button_x < button_w - views::kDropBetweenPixels) { 1680 location->index = i; 1681 location->on = true; 1682 } else { 1683 location->index = i + 1; 1684 } 1685 } else if (button_x < button_w / 2) { 1686 location->index = i; 1687 } else { 1688 location->index = i + 1; 1689 } 1690 break; 1691 } 1692 } 1693 1694 if (!found) { 1695 if (overflow_button_->visible()) { 1696 // Are we over the overflow button? 1697 int overflow_delta_x = mirrored_x - overflow_button_->x(); 1698 if (overflow_delta_x >= 0 && 1699 overflow_delta_x < overflow_button_->width()) { 1700 // Mouse is over overflow button. 1701 location->index = GetFirstHiddenNodeIndex(); 1702 location->button_type = DROP_OVERFLOW; 1703 } else if (overflow_delta_x < 0) { 1704 // Mouse is after the last visible button but before overflow button; 1705 // use the last visible index. 1706 location->index = GetFirstHiddenNodeIndex(); 1707 } else { 1708 return; 1709 } 1710 } else if (!other_bookmarked_button_->visible() || 1711 mirrored_x < other_bookmarked_button_->x()) { 1712 // Mouse is after the last visible button but before more recently 1713 // bookmarked; use the last visible index. 1714 location->index = GetFirstHiddenNodeIndex(); 1715 } else { 1716 return; 1717 } 1718 } 1719 1720 if (location->on) { 1721 const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ? 1722 model_->other_node() : 1723 model_->bookmark_bar_node()->GetChild(location->index); 1724 location->operation = chrome::GetBookmarkDropOperation( 1725 profile, event, data, parent, parent->child_count()); 1726 if (!location->operation && !data.has_single_url() && 1727 data.GetFirstNode(model_, profile->GetPath()) == parent) { 1728 // Don't open a menu if the node being dragged is the menu to open. 1729 location->on = false; 1730 } 1731 } else { 1732 location->operation = chrome::GetBookmarkDropOperation( 1733 profile, event, data, model_->bookmark_bar_node(), location->index); 1734 } 1735 } 1736 1737 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node, 1738 ui::OSExchangeData* data) { 1739 DCHECK(node && data); 1740 BookmarkNodeData drag_data(node); 1741 drag_data.Write(browser_->profile()->GetPath(), data); 1742 } 1743 1744 void BookmarkBarView::StartThrobbing(const BookmarkNode* node, 1745 bool overflow_only) { 1746 DCHECK(!throbbing_view_); 1747 1748 // Determine which visible button is showing the bookmark (or is an ancestor 1749 // of the bookmark). 1750 const BookmarkNode* bbn = model_->bookmark_bar_node(); 1751 const BookmarkNode* parent_on_bb = node; 1752 while (parent_on_bb) { 1753 const BookmarkNode* parent = parent_on_bb->parent(); 1754 if (parent == bbn) 1755 break; 1756 parent_on_bb = parent; 1757 } 1758 if (parent_on_bb) { 1759 int index = bbn->GetIndexOf(parent_on_bb); 1760 if (index >= GetFirstHiddenNodeIndex()) { 1761 // Node is hidden, animate the overflow button. 1762 throbbing_view_ = overflow_button_; 1763 } else if (!overflow_only) { 1764 throbbing_view_ = static_cast<CustomButton*>(child_at(index)); 1765 } 1766 } else if (client_->IsDescendantOfManagedNode(node)) { 1767 throbbing_view_ = managed_bookmarks_button_; 1768 } else if (!overflow_only) { 1769 throbbing_view_ = other_bookmarked_button_; 1770 } 1771 1772 // Use a large number so that the button continues to throb. 1773 if (throbbing_view_) 1774 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max()); 1775 } 1776 1777 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove( 1778 const BookmarkNode* parent, 1779 int old_index) { 1780 const BookmarkNode* bbn = model_->bookmark_bar_node(); 1781 const BookmarkNode* old_node = parent; 1782 int old_index_on_bb = old_index; 1783 while (old_node && old_node != bbn) { 1784 const BookmarkNode* parent = old_node->parent(); 1785 if (parent == bbn) { 1786 old_index_on_bb = bbn->GetIndexOf(old_node); 1787 break; 1788 } 1789 old_node = parent; 1790 } 1791 if (old_node) { 1792 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) { 1793 // Node is hidden, animate the overflow button. 1794 return overflow_button_; 1795 } 1796 return static_cast<CustomButton*>(child_at(old_index_on_bb)); 1797 } 1798 if (client_->IsDescendantOfManagedNode(parent)) 1799 return managed_bookmarks_button_; 1800 // Node wasn't on the bookmark bar, use the other bookmark button. 1801 return other_bookmarked_button_; 1802 } 1803 1804 void BookmarkBarView::UpdateColors() { 1805 // We don't always have a theme provider (ui tests, for example). 1806 const ui::ThemeProvider* theme_provider = GetThemeProvider(); 1807 if (!theme_provider) 1808 return; 1809 SkColor color = 1810 theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT); 1811 for (int i = 0; i < GetBookmarkButtonCount(); ++i) 1812 GetBookmarkButton(i)->SetTextColor(views::Button::STATE_NORMAL, color); 1813 other_bookmarked_button_->SetTextColor(views::Button::STATE_NORMAL, color); 1814 managed_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color); 1815 if (apps_page_shortcut_->visible()) 1816 apps_page_shortcut_->SetTextColor(views::Button::STATE_NORMAL, color); 1817 } 1818 1819 void BookmarkBarView::UpdateButtonsVisibility() { 1820 bool has_other_children = !model_->other_node()->empty(); 1821 bool update_other = has_other_children != other_bookmarked_button_->visible(); 1822 if (update_other) { 1823 other_bookmarked_button_->SetVisible(has_other_children); 1824 UpdateBookmarksSeparatorVisibility(); 1825 } 1826 1827 bool show_managed = !client_->managed_node()->empty() && 1828 browser_->profile()->GetPrefs()->GetBoolean( 1829 prefs::kShowManagedBookmarksInBookmarkBar); 1830 bool update_managed = show_managed != managed_bookmarks_button_->visible(); 1831 if (update_managed) 1832 managed_bookmarks_button_->SetVisible(show_managed); 1833 1834 if (update_other || update_managed) { 1835 Layout(); 1836 SchedulePaint(); 1837 } 1838 } 1839 1840 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() { 1841 // Ash does not paint the bookmarks separator line because it looks odd on 1842 // the flat background. We keep it present for layout, but don't draw it. 1843 bookmarks_separator_view_->SetVisible( 1844 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH && 1845 other_bookmarked_button_->visible()); 1846 } 1847 1848 void BookmarkBarView::LayoutItems() { 1849 if (!parent()) 1850 return; 1851 1852 int x = kLeftMargin; 1853 int top_margin = IsDetached() ? kDetachedTopMargin : 0; 1854 int y = top_margin; 1855 int width = View::width() - kRightMargin - kLeftMargin; 1856 int height = chrome::kBookmarkBarHeight - kBottomMargin; 1857 int separator_margin = kSeparatorMargin; 1858 1859 if (IsDetached()) { 1860 double current_state = 1 - size_animation_->GetCurrentValue(); 1861 x += static_cast<int>(kNewtabHorizontalPadding * current_state); 1862 y += (View::height() - chrome::kBookmarkBarHeight) / 2; 1863 width -= static_cast<int>(kNewtabHorizontalPadding * current_state); 1864 separator_margin -= static_cast<int>(kSeparatorMargin * current_state); 1865 } else { 1866 // For the attached appearance, pin the content to the bottom of the bar 1867 // when animating in/out, as shrinking its height instead looks weird. This 1868 // also matches how we layout infobars. 1869 y += View::height() - chrome::kBookmarkBarHeight; 1870 } 1871 1872 gfx::Size other_bookmarked_pref = other_bookmarked_button_->visible() ? 1873 other_bookmarked_button_->GetPreferredSize() : gfx::Size(); 1874 gfx::Size overflow_pref = overflow_button_->GetPreferredSize(); 1875 gfx::Size bookmarks_separator_pref = 1876 bookmarks_separator_view_->GetPreferredSize(); 1877 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ? 1878 apps_page_shortcut_->GetPreferredSize() : gfx::Size(); 1879 1880 int max_x = width - overflow_pref.width() - kButtonPadding - 1881 bookmarks_separator_pref.width(); 1882 if (other_bookmarked_button_->visible()) 1883 max_x -= other_bookmarked_pref.width() + kButtonPadding; 1884 1885 // Next, layout out the buttons. Any buttons that are placed beyond the 1886 // visible region are made invisible. 1887 1888 // Start with the apps page shortcut button. 1889 if (apps_page_shortcut_->visible()) { 1890 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(), 1891 height); 1892 x += apps_page_shortcut_pref.width() + kButtonPadding; 1893 } 1894 1895 // Then comes the managed bookmarks folder, if visible. 1896 if (managed_bookmarks_button_->visible()) { 1897 gfx::Size managed_bookmarks_pref = managed_bookmarks_button_->visible() ? 1898 managed_bookmarks_button_->GetPreferredSize() : gfx::Size(); 1899 managed_bookmarks_button_->SetBounds(x, y, managed_bookmarks_pref.width(), 1900 height); 1901 x += managed_bookmarks_pref.width() + kButtonPadding; 1902 } 1903 1904 // Then go through the bookmark buttons. 1905 if (GetBookmarkButtonCount() == 0 && model_ && model_->loaded()) { 1906 gfx::Size pref = instructions_->GetPreferredSize(); 1907 instructions_->SetBounds( 1908 x + kInstructionsPadding, y, 1909 std::min(static_cast<int>(pref.width()), 1910 max_x - x), 1911 height); 1912 instructions_->SetVisible(true); 1913 } else { 1914 instructions_->SetVisible(false); 1915 1916 for (int i = 0; i < GetBookmarkButtonCount(); ++i) { 1917 views::View* child = child_at(i); 1918 gfx::Size pref = child->GetPreferredSize(); 1919 int next_x = x + pref.width() + kButtonPadding; 1920 child->SetVisible(next_x < max_x); 1921 child->SetBounds(x, y, pref.width(), height); 1922 x = next_x; 1923 } 1924 } 1925 1926 // Layout the right side of the bar. 1927 const bool all_visible = (GetBookmarkButtonCount() == 0 || 1928 child_at(GetBookmarkButtonCount() - 1)->visible()); 1929 1930 // Layout the right side buttons. 1931 x = max_x + kButtonPadding; 1932 1933 // The overflow button. 1934 overflow_button_->SetBounds(x, y, overflow_pref.width(), height); 1935 overflow_button_->SetVisible(!all_visible); 1936 x += overflow_pref.width(); 1937 1938 // Separator. 1939 if (bookmarks_separator_view_->visible()) { 1940 bookmarks_separator_view_->SetBounds(x, 1941 y - top_margin, 1942 bookmarks_separator_pref.width(), 1943 height + top_margin + kBottomMargin - 1944 separator_margin); 1945 1946 x += bookmarks_separator_pref.width(); 1947 } 1948 1949 // The other bookmarks button. 1950 if (other_bookmarked_button_->visible()) { 1951 other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.width(), 1952 height); 1953 x += other_bookmarked_pref.width() + kButtonPadding; 1954 } 1955 } 1956 1957 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() { 1958 DCHECK(apps_page_shortcut_); 1959 // Only perform layout if required. 1960 bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar( 1961 browser_->profile(), browser_->host_desktop_type()); 1962 if (apps_page_shortcut_->visible() == visible) 1963 return; 1964 apps_page_shortcut_->SetVisible(visible); 1965 UpdateBookmarksSeparatorVisibility(); 1966 Layout(); 1967 } 1968