Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bar_view.cc
blob7f3c05b0a37c6ec475120e7af6b90cd28d347cbb
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.
5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
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_bar_view_observer.h"
42 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
43 #include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h"
44 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
45 #include "chrome/browser/ui/views/event_utils.h"
46 #include "chrome/browser/ui/views/frame/browser_view.h"
47 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
48 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "chrome/common/extensions/extension_constants.h"
51 #include "chrome/common/pref_names.h"
52 #include "chrome/common/url_constants.h"
53 #include "chrome/grit/generated_resources.h"
54 #include "components/bookmarks/browser/bookmark_model.h"
55 #include "components/metrics/metrics_service.h"
56 #include "content/public/browser/notification_details.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/page_navigator.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/render_widget_host_view.h"
61 #include "content/public/browser/user_metrics.h"
62 #include "content/public/browser/web_contents.h"
63 #include "extensions/browser/extension_registry.h"
64 #include "extensions/common/extension.h"
65 #include "extensions/common/extension_set.h"
66 #include "grit/theme_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/page_transition_types.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/resources/grit/ui_resources.h"
80 #include "ui/views/button_drag_utils.h"
81 #include "ui/views/controls/button/label_button.h"
82 #include "ui/views/controls/button/label_button_border.h"
83 #include "ui/views/controls/button/menu_button.h"
84 #include "ui/views/controls/label.h"
85 #include "ui/views/drag_utils.h"
86 #include "ui/views/metrics.h"
87 #include "ui/views/view_constants.h"
88 #include "ui/views/widget/tooltip_manager.h"
89 #include "ui/views/widget/widget.h"
90 #include "ui/views/window/non_client_view.h"
92 using base::UserMetricsAction;
93 using bookmarks::BookmarkNodeData;
94 using content::OpenURLParams;
95 using content::PageNavigator;
96 using content::Referrer;
97 using ui::DropTargetEvent;
98 using views::CustomButton;
99 using views::LabelButtonBorder;
100 using views::MenuButton;
101 using views::View;
103 // Margins around the content.
104 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the
105 // toolbar above serve as the margin.
106 static const int kBottomMargin = 2;
107 static const int kLeftMargin = 1;
108 static const int kRightMargin = 1;
110 // static
111 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView";
113 // Padding between buttons.
114 static const int kButtonPadding = 0;
116 // Icon to display when one isn't found for the page.
117 static gfx::ImageSkia* kDefaultFavicon = NULL;
119 // Icon used for folders.
120 static gfx::ImageSkia* kFolderIcon = NULL;
122 // Color of the drop indicator.
123 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
125 // Width of the drop indicator.
126 static const int kDropIndicatorWidth = 2;
128 // Distance between the bottom of the bar and the separator.
129 static const int kSeparatorMargin = 1;
131 // Width of the separator between the recently bookmarked button and the
132 // overflow indicator.
133 static const int kSeparatorWidth = 4;
135 // Starting x-coordinate of the separator line within a separator.
136 static const int kSeparatorStartX = 2;
138 // Left-padding for the instructional text.
139 static const int kInstructionsPadding = 6;
141 // Tag for the 'Other bookmarks' button.
142 static const int kOtherFolderButtonTag = 1;
144 // Tag for the 'Apps Shortcut' button.
145 static const int kAppsShortcutButtonTag = 2;
147 // Preferred padding between text and edge.
148 static const int kButtonPaddingHorizontal = 6;
149 static const int kButtonPaddingVertical = 4;
151 // Tag for the 'Managed bookmarks' button.
152 static const int kManagedFolderButtonTag = 3;
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
161 namespace {
163 // To enable/disable BookmarkBar animations during testing. In production
164 // animations are enabled by default.
165 bool animations_enabled = true;
167 // BookmarkButtonBase -----------------------------------------------
169 // Base class for buttons used on the bookmark bar.
171 class BookmarkButtonBase : public views::LabelButton {
172 public:
173 BookmarkButtonBase(views::ButtonListener* listener,
174 const base::string16& title)
175 : LabelButton(listener, title) {
176 SetElideBehavior(kElideBehavior);
177 show_animation_.reset(new gfx::SlideAnimation(this));
178 if (!animations_enabled) {
179 // For some reason during testing the events generated by animating
180 // throw off the test. So, don't animate while testing.
181 show_animation_->Reset(1);
182 } else {
183 show_animation_->Show();
187 virtual View* GetTooltipHandlerForPoint(const gfx::Point& point) override {
188 return HitTestPoint(point) && CanProcessEventsWithinSubtree() ? this : NULL;
191 virtual scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const override {
192 scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder();
193 border->set_insets(gfx::Insets(kButtonPaddingVertical,
194 kButtonPaddingHorizontal,
195 kButtonPaddingVertical,
196 kButtonPaddingHorizontal));
197 return border.Pass();
200 virtual bool IsTriggerableEvent(const ui::Event& e) override {
201 return e.type() == ui::ET_GESTURE_TAP ||
202 e.type() == ui::ET_GESTURE_TAP_DOWN ||
203 event_utils::IsPossibleDispositionEvent(e);
206 private:
207 scoped_ptr<gfx::SlideAnimation> show_animation_;
209 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase);
212 // BookmarkButton -------------------------------------------------------------
214 // Buttons used for the bookmarks on the bookmark bar.
216 class BookmarkButton : public BookmarkButtonBase {
217 public:
218 // The internal view class name.
219 static const char kViewClassName[];
221 BookmarkButton(views::ButtonListener* listener,
222 const GURL& url,
223 const base::string16& title,
224 Profile* profile)
225 : BookmarkButtonBase(listener, title),
226 url_(url),
227 profile_(profile) {
230 virtual bool GetTooltipText(const gfx::Point& p,
231 base::string16* tooltip) const override {
232 gfx::Point location(p);
233 ConvertPointToScreen(this, &location);
234 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle(
235 GetWidget(), location, url_, GetText(), profile_);
236 return !tooltip->empty();
239 virtual const char* GetClassName() const override {
240 return kViewClassName;
243 private:
244 const GURL& url_;
245 Profile* profile_;
247 DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
250 // static
251 const char BookmarkButton::kViewClassName[] = "BookmarkButton";
253 // ShortcutButton -------------------------------------------------------------
255 // Buttons used for the shortcuts on the bookmark bar.
257 class ShortcutButton : public BookmarkButtonBase {
258 public:
259 // The internal view class name.
260 static const char kViewClassName[];
262 ShortcutButton(views::ButtonListener* listener,
263 const base::string16& title)
264 : BookmarkButtonBase(listener, title) {
267 virtual const char* GetClassName() const override {
268 return kViewClassName;
271 private:
272 DISALLOW_COPY_AND_ASSIGN(ShortcutButton);
275 // static
276 const char ShortcutButton::kViewClassName[] = "ShortcutButton";
278 // BookmarkFolderButton -------------------------------------------------------
280 // Buttons used for folders on the bookmark bar, including the 'other folders'
281 // button.
282 class 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();
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();
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;
316 if (e.IsMouseEvent())
317 return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB;
318 return false;
321 private:
322 scoped_ptr<gfx::SlideAnimation> show_animation_;
324 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
327 // OverFlowButton (chevron) --------------------------------------------------
329 class OverFlowButton : public views::MenuButton {
330 public:
331 explicit OverFlowButton(BookmarkBarView* owner)
332 : MenuButton(NULL, base::string16(), owner, false),
333 owner_(owner) {}
335 virtual bool OnMousePressed(const ui::MouseEvent& e) override {
336 owner_->StopThrobbing(true);
337 return views::MenuButton::OnMousePressed(e);
340 private:
341 BookmarkBarView* owner_;
343 DISALLOW_COPY_AND_ASSIGN(OverFlowButton);
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;
353 CoreAppLauncherHandler::RecordAppLaunchType(
354 extension_misc::APP_LAUNCH_BOOKMARK_BAR,
355 extension->GetType());
358 } // namespace
360 // DropLocation ---------------------------------------------------------------
362 struct BookmarkBarView::DropLocation {
363 DropLocation()
364 : index(-1),
365 operation(ui::DragDropTypes::DRAG_NONE),
366 on(false),
367 button_type(DROP_BOOKMARK) {
370 bool Equals(const DropLocation& other) {
371 return ((other.index == index) && (other.on == on) &&
372 (other.button_type == button_type));
375 // Index into the model the drop is over. This is relative to the root node.
376 int index;
378 // Drop constants.
379 int operation;
381 // If true, the user is dropping on a folder.
382 bool on;
384 // Type of button.
385 DropButtonType button_type;
388 // DropInfo -------------------------------------------------------------------
390 // Tracks drops on the BookmarkBarView.
392 struct BookmarkBarView::DropInfo {
393 DropInfo()
394 : valid(false),
395 is_menu_showing(false),
396 x(0),
397 y(0) {
400 // Whether the data is valid.
401 bool valid;
403 // If true, the menu is being shown.
404 bool is_menu_showing;
406 // Coordinates of the drag (in terms of the BookmarkBarView).
407 int x;
408 int y;
410 // DropData for the drop.
411 BookmarkNodeData data;
413 DropLocation location;
416 // ButtonSeparatorView --------------------------------------------------------
418 class BookmarkBarView::ButtonSeparatorView : public views::View {
419 public:
420 ButtonSeparatorView() {}
421 virtual ~ButtonSeparatorView() {}
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));
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);
437 virtual void GetAccessibleState(ui::AXViewState* state) override {
438 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR);
439 state->role = ui::AX_ROLE_SPLITTER;
442 private:
443 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
446 // BookmarkBarView ------------------------------------------------------------
448 // static
449 const int BookmarkBarView::kMaxButtonWidth = 150;
450 const int BookmarkBarView::kNewtabHorizontalPadding = 2;
451 const int BookmarkBarView::kToolbarAttachedBookmarkBarOverlap = 3;
453 const gfx::ImageSkia& GetDefaultFavicon() {
454 if (!kDefaultFavicon) {
455 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
456 kDefaultFavicon = rb->GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
458 return *kDefaultFavicon;
461 const gfx::ImageSkia& GetFolderIcon() {
462 if (!kFolderIcon) {
463 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
464 kFolderIcon = rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER);
466 return *kFolderIcon;
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 overflow_button_(NULL),
478 instructions_(NULL),
479 bookmarks_separator_view_(NULL),
480 browser_(browser),
481 browser_view_(browser_view),
482 infobar_visible_(false),
483 throbbing_view_(NULL),
484 bookmark_bar_state_(BookmarkBar::SHOW),
485 animating_detached_(false),
486 show_folder_method_factory_(this) {
487 set_id(VIEW_ID_BOOKMARK_BAR);
488 Init();
490 size_animation_->Reset(1);
493 BookmarkBarView::~BookmarkBarView() {
494 if (model_)
495 model_->RemoveObserver(this);
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();
504 if (context_menu_.get())
505 context_menu_->SetPageNavigator(NULL);
507 StopShowFolderDropMenuTimer();
510 // static
511 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) {
512 animations_enabled = !disabled;
515 void BookmarkBarView::AddObserver(BookmarkBarViewObserver* observer) {
516 observers_.AddObserver(observer);
519 void BookmarkBarView::RemoveObserver(BookmarkBarViewObserver* observer) {
520 observers_.RemoveObserver(observer);
523 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
524 page_navigator_ = navigator;
525 if (bookmark_menu_)
526 bookmark_menu_->SetPageNavigator(navigator);
527 if (context_menu_.get())
528 context_menu_->SetPageNavigator(navigator);
531 void BookmarkBarView::SetBookmarkBarState(
532 BookmarkBar::State state,
533 BookmarkBar::AnimateChangeType animate_type) {
534 if (animate_type == BookmarkBar::ANIMATE_STATE_CHANGE &&
535 animations_enabled) {
536 animating_detached_ = (state == BookmarkBar::DETACHED ||
537 bookmark_bar_state_ == BookmarkBar::DETACHED);
538 if (state == BookmarkBar::SHOW)
539 size_animation_->Show();
540 else
541 size_animation_->Hide();
542 } else {
543 size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0);
545 bookmark_bar_state_ = state;
548 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const {
549 if (!infobar_visible_ && browser_->window()->IsFullscreen()) {
550 // There is no client edge to overlap when detached in fullscreen with no
551 // infobars visible.
552 return 0;
554 return views::NonClientFrameView::kClientEdgeThickness;
557 bool BookmarkBarView::is_animating() {
558 return size_animation_->is_animating();
561 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
562 const gfx::Point& loc,
563 int* model_start_index) {
564 *model_start_index = 0;
566 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
567 return NULL;
569 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
571 // Check the managed button first.
572 if (managed_bookmarks_button_->visible() &&
573 managed_bookmarks_button_->bounds().Contains(adjusted_loc)) {
574 return client_->managed_node();
577 // Then check the bookmark buttons.
578 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
579 views::View* child = child_at(i);
580 if (!child->visible())
581 break;
582 if (child->bounds().Contains(adjusted_loc))
583 return model_->bookmark_bar_node()->GetChild(i);
586 // Then the overflow button.
587 if (overflow_button_->visible() &&
588 overflow_button_->bounds().Contains(adjusted_loc)) {
589 *model_start_index = GetFirstHiddenNodeIndex();
590 return model_->bookmark_bar_node();
593 // And finally the other folder.
594 if (other_bookmarked_button_->visible() &&
595 other_bookmarked_button_->bounds().Contains(adjusted_loc)) {
596 return model_->other_node();
599 return NULL;
602 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
603 const BookmarkNode* node) {
604 if (node == client_->managed_node())
605 return managed_bookmarks_button_;
606 if (node == model_->other_node())
607 return other_bookmarked_button_;
608 if (node == model_->bookmark_bar_node())
609 return overflow_button_;
610 int index = model_->bookmark_bar_node()->GetIndexOf(node);
611 if (index == -1 || !node->is_folder())
612 return NULL;
613 return static_cast<views::MenuButton*>(child_at(index));
616 void BookmarkBarView::GetAnchorPositionForButton(
617 views::MenuButton* button,
618 views::MenuAnchorPosition* anchor) {
619 if (button == other_bookmarked_button_ || button == overflow_button_)
620 *anchor = views::MENU_ANCHOR_TOPRIGHT;
621 else
622 *anchor = views::MENU_ANCHOR_TOPLEFT;
625 views::MenuItemView* BookmarkBarView::GetMenu() {
626 return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
629 views::MenuItemView* BookmarkBarView::GetContextMenu() {
630 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
633 views::MenuItemView* BookmarkBarView::GetDropMenu() {
634 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
637 void BookmarkBarView::StopThrobbing(bool immediate) {
638 if (!throbbing_view_)
639 return;
641 // If not immediate, cycle through 2 more complete cycles.
642 throbbing_view_->StartThrobbing(immediate ? 0 : 4);
643 throbbing_view_ = NULL;
646 // static
647 base::string16 BookmarkBarView::CreateToolTipForURLAndTitle(
648 const views::Widget* widget,
649 const gfx::Point& screen_loc,
650 const GURL& url,
651 const base::string16& title,
652 Profile* profile) {
653 int max_width = views::TooltipManager::GetMaxWidth(
654 screen_loc.x(),
655 screen_loc.y(),
656 widget->GetNativeView());
657 const gfx::FontList tt_fonts = widget->GetTooltipManager()->GetFontList();
658 base::string16 result;
660 // First the title.
661 if (!title.empty()) {
662 base::string16 localized_title = title;
663 base::i18n::AdjustStringForLocaleDirection(&localized_title);
664 result.append(gfx::ElideText(localized_title, tt_fonts, max_width,
665 gfx::ELIDE_TAIL));
668 // Only show the URL if the url and title differ.
669 if (title != base::UTF8ToUTF16(url.spec())) {
670 if (!result.empty())
671 result.push_back('\n');
673 // We need to explicitly specify the directionality of the URL's text to
674 // make sure it is treated as an LTR string when the context is RTL. For
675 // example, the URL "http://www.yahoo.com/" appears as
676 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
677 // the Unicode BiDi algorithm puts certain characters on the left by
678 // default.
679 std::string languages = profile->GetPrefs()->GetString(
680 prefs::kAcceptLanguages);
681 base::string16 elided_url(ElideUrl(url, tt_fonts, max_width, languages));
682 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
683 result.append(elided_url);
685 return result;
688 bool BookmarkBarView::IsDetached() const {
689 return (bookmark_bar_state_ == BookmarkBar::DETACHED) ||
690 (animating_detached_ && size_animation_->is_animating());
693 double BookmarkBarView::GetAnimationValue() const {
694 return size_animation_->GetCurrentValue();
697 int BookmarkBarView::GetToolbarOverlap() const {
698 int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
699 views::NonClientFrameView::kClientEdgeThickness;
700 if (!IsDetached())
701 return attached_overlap;
703 int detached_overlap = GetFullyDetachedToolbarOverlap();
705 // Do not animate the overlap when the infobar is above us (i.e. when we're
706 // detached), since drawing over the infobar looks weird.
707 if (infobar_visible_)
708 return detached_overlap;
710 // When detached with no infobar, animate the overlap between the attached and
711 // detached states.
712 return detached_overlap + static_cast<int>(
713 (attached_overlap - detached_overlap) *
714 size_animation_->GetCurrentValue());
717 gfx::Size BookmarkBarView::GetPreferredSize() const {
718 gfx::Size prefsize;
719 if (IsDetached()) {
720 prefsize.set_height(
721 chrome::kBookmarkBarHeight +
722 static_cast<int>(
723 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
724 (1 - size_animation_->GetCurrentValue())));
725 } else {
726 prefsize.set_height(static_cast<int>(chrome::kBookmarkBarHeight *
727 size_animation_->GetCurrentValue()));
729 return prefsize;
732 bool BookmarkBarView::CanProcessEventsWithinSubtree() const {
733 // If the bookmark bar is attached and the omnibox popup is open (on top of
734 // the bar), prevent events from targeting the bookmark bar or any of its
735 // descendants. This will prevent hovers/clicks just above the omnibox popup
736 // from activating the top few pixels of items on the bookmark bar.
737 if (!IsDetached() && browser_view_ &&
738 browser_view_->GetLocationBar()->GetOmniboxView()->model()->
739 popup_model()->IsOpen()) {
740 return false;
742 return true;
745 gfx::Size BookmarkBarView::GetMinimumSize() const {
746 // The minimum width of the bookmark bar should at least contain the overflow
747 // button, by which one can access all the Bookmark Bar items, and the "Other
748 // Bookmarks" folder, along with appropriate margins and button padding.
749 // It should also contain the Managed Bookmarks folder, if it's visible.
750 int width = kLeftMargin;
752 int height = chrome::kBookmarkBarHeight;
753 if (IsDetached()) {
754 double current_state = 1 - size_animation_->GetCurrentValue();
755 width += 2 * static_cast<int>(kNewtabHorizontalPadding * current_state);
756 height += static_cast<int>(
757 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
758 current_state);
761 if (managed_bookmarks_button_->visible()) {
762 gfx::Size size = managed_bookmarks_button_->GetPreferredSize();
763 width += size.width() + kButtonPadding;
765 if (other_bookmarked_button_->visible()) {
766 gfx::Size size = other_bookmarked_button_->GetPreferredSize();
767 width += size.width() + kButtonPadding;
769 if (overflow_button_->visible()) {
770 gfx::Size size = overflow_button_->GetPreferredSize();
771 width += size.width() + kButtonPadding;
773 if (bookmarks_separator_view_->visible()) {
774 gfx::Size size = bookmarks_separator_view_->GetPreferredSize();
775 width += size.width();
777 if (apps_page_shortcut_->visible()) {
778 gfx::Size size = apps_page_shortcut_->GetPreferredSize();
779 width += size.width() + kButtonPadding;
782 return gfx::Size(width, height);
785 void BookmarkBarView::Layout() {
786 int x = kLeftMargin;
787 int top_margin = IsDetached() ? kDetachedTopMargin : 0;
788 int y = top_margin;
789 int width = View::width() - kRightMargin - kLeftMargin;
790 int height = chrome::kBookmarkBarHeight - kBottomMargin;
791 int separator_margin = kSeparatorMargin;
793 if (IsDetached()) {
794 double current_state = 1 - size_animation_->GetCurrentValue();
795 x += static_cast<int>(kNewtabHorizontalPadding * current_state);
796 y += (View::height() - chrome::kBookmarkBarHeight) / 2;
797 width -= static_cast<int>(kNewtabHorizontalPadding * current_state);
798 separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
799 } else {
800 // For the attached appearance, pin the content to the bottom of the bar
801 // when animating in/out, as shrinking its height instead looks weird. This
802 // also matches how we layout infobars.
803 y += View::height() - chrome::kBookmarkBarHeight;
806 gfx::Size other_bookmarked_pref = other_bookmarked_button_->visible() ?
807 other_bookmarked_button_->GetPreferredSize() : gfx::Size();
808 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
809 gfx::Size bookmarks_separator_pref =
810 bookmarks_separator_view_->GetPreferredSize();
811 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ?
812 apps_page_shortcut_->GetPreferredSize() : gfx::Size();
814 int max_x = width - overflow_pref.width() - kButtonPadding -
815 bookmarks_separator_pref.width();
816 if (other_bookmarked_button_->visible())
817 max_x -= other_bookmarked_pref.width() + kButtonPadding;
819 // Next, layout out the buttons. Any buttons that are placed beyond the
820 // visible region are made invisible.
822 // Start with the apps page shortcut button.
823 if (apps_page_shortcut_->visible()) {
824 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(),
825 height);
826 x += apps_page_shortcut_pref.width() + kButtonPadding;
829 // Then comes the managed bookmarks folder, if visible.
830 if (managed_bookmarks_button_->visible()) {
831 gfx::Size managed_bookmarks_pref = managed_bookmarks_button_->visible() ?
832 managed_bookmarks_button_->GetPreferredSize() : gfx::Size();
833 managed_bookmarks_button_->SetBounds(x, y, managed_bookmarks_pref.width(),
834 height);
835 x += managed_bookmarks_pref.width() + kButtonPadding;
838 // Then go through the bookmark buttons.
839 if (GetBookmarkButtonCount() == 0 && model_ && model_->loaded()) {
840 gfx::Size pref = instructions_->GetPreferredSize();
841 instructions_->SetBounds(
842 x + kInstructionsPadding, y,
843 std::min(static_cast<int>(pref.width()),
844 max_x - x),
845 height);
846 instructions_->SetVisible(true);
847 } else {
848 instructions_->SetVisible(false);
850 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
851 views::View* child = child_at(i);
852 gfx::Size pref = child->GetPreferredSize();
853 int next_x = x + pref.width() + kButtonPadding;
854 child->SetVisible(next_x < max_x);
855 child->SetBounds(x, y, pref.width(), height);
856 x = next_x;
860 // Layout the right side of the bar.
861 const bool all_visible = (GetBookmarkButtonCount() == 0 ||
862 child_at(GetBookmarkButtonCount() - 1)->visible());
864 // Layout the right side buttons.
865 x = max_x + kButtonPadding;
867 // The overflow button.
868 overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
869 overflow_button_->SetVisible(!all_visible);
870 x += overflow_pref.width();
872 // Separator.
873 if (bookmarks_separator_view_->visible()) {
874 bookmarks_separator_view_->SetBounds(x,
875 y - top_margin,
876 bookmarks_separator_pref.width(),
877 height + top_margin + kBottomMargin -
878 separator_margin);
880 x += bookmarks_separator_pref.width();
883 // The other bookmarks button.
884 if (other_bookmarked_button_->visible()) {
885 other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.width(),
886 height);
887 x += other_bookmarked_pref.width() + kButtonPadding;
891 void BookmarkBarView::ViewHierarchyChanged(
892 const ViewHierarchyChangedDetails& details) {
893 if (details.is_add && details.child == this) {
894 // We may get inserted into a hierarchy with a profile - this typically
895 // occurs when the bar's contents get populated fast enough that the
896 // buttons are created before the bar is attached to a frame.
897 UpdateColors();
899 if (height() > 0) {
900 // We only layout while parented. When we become parented, if our bounds
901 // haven't changed, OnBoundsChanged() won't get invoked and we won't
902 // layout. Therefore we always force a layout when added.
903 Layout();
908 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas,
909 const views::CullSet& cull_set) {
910 View::PaintChildren(canvas, cull_set);
912 if (drop_info_.get() && drop_info_->valid &&
913 drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
914 drop_info_->location.button_type != DROP_OVERFLOW &&
915 !drop_info_->location.on) {
916 int index = drop_info_->location.index;
917 DCHECK(index <= GetBookmarkButtonCount());
918 int x = 0;
919 int y = 0;
920 int h = height();
921 if (index == GetBookmarkButtonCount()) {
922 if (index == 0) {
923 x = kLeftMargin;
924 } else {
925 x = GetBookmarkButton(index - 1)->x() +
926 GetBookmarkButton(index - 1)->width();
928 } else {
929 x = GetBookmarkButton(index)->x();
931 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
932 y = GetBookmarkButton(0)->y();
933 h = GetBookmarkButton(0)->height();
936 // Since the drop indicator is painted directly onto the canvas, we must
937 // make sure it is painted in the right location if the locale is RTL.
938 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
940 kDropIndicatorWidth,
942 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
944 // TODO(sky/glen): make me pretty!
945 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
949 bool BookmarkBarView::GetDropFormats(
950 int* formats,
951 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
952 if (!model_ || !model_->loaded())
953 return false;
954 *formats = ui::OSExchangeData::URL;
955 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat());
956 return true;
959 bool BookmarkBarView::AreDropTypesRequired() {
960 return true;
963 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
964 if (!model_ || !model_->loaded() ||
965 !browser_->profile()->GetPrefs()->GetBoolean(
966 bookmarks::prefs::kEditBookmarksEnabled))
967 return false;
969 if (!drop_info_.get())
970 drop_info_.reset(new DropInfo());
972 // Only accept drops of 1 node, which is the case for all data dragged from
973 // bookmark bar and menus.
974 return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
977 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
980 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
981 if (!drop_info_.get())
982 return 0;
984 if (drop_info_->valid &&
985 (drop_info_->x == event.x() && drop_info_->y == event.y())) {
986 // The location of the mouse didn't change, return the last operation.
987 return drop_info_->location.operation;
990 drop_info_->x = event.x();
991 drop_info_->y = event.y();
993 DropLocation location;
994 CalculateDropLocation(event, drop_info_->data, &location);
996 if (drop_info_->valid && drop_info_->location.Equals(location)) {
997 // The position we're going to drop didn't change, return the last drag
998 // operation we calculated. Copy of the operation in case it changed.
999 drop_info_->location.operation = location.operation;
1000 return drop_info_->location.operation;
1003 StopShowFolderDropMenuTimer();
1005 // TODO(sky): Optimize paint region.
1006 SchedulePaint();
1008 drop_info_->location = location;
1009 drop_info_->valid = true;
1011 if (drop_info_->is_menu_showing) {
1012 if (bookmark_drop_menu_)
1013 bookmark_drop_menu_->Cancel();
1014 drop_info_->is_menu_showing = false;
1017 if (location.on || location.button_type == DROP_OVERFLOW ||
1018 location.button_type == DROP_OTHER_FOLDER) {
1019 const BookmarkNode* node;
1020 if (location.button_type == DROP_OTHER_FOLDER)
1021 node = model_->other_node();
1022 else if (location.button_type == DROP_OVERFLOW)
1023 node = model_->bookmark_bar_node();
1024 else
1025 node = model_->bookmark_bar_node()->GetChild(location.index);
1026 StartShowFolderDropMenuTimer(node);
1029 return drop_info_->location.operation;
1032 void BookmarkBarView::OnDragExited() {
1033 StopShowFolderDropMenuTimer();
1035 // NOTE: we don't hide the menu on exit as it's possible the user moved the
1036 // mouse over the menu, which triggers an exit on us.
1038 drop_info_->valid = false;
1040 if (drop_info_->location.index != -1) {
1041 // TODO(sky): optimize the paint region.
1042 SchedulePaint();
1044 drop_info_.reset();
1047 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
1048 StopShowFolderDropMenuTimer();
1050 if (bookmark_drop_menu_)
1051 bookmark_drop_menu_->Cancel();
1053 if (!drop_info_.get() || !drop_info_->location.operation)
1054 return ui::DragDropTypes::DRAG_NONE;
1056 const BookmarkNode* root =
1057 (drop_info_->location.button_type == DROP_OTHER_FOLDER) ?
1058 model_->other_node() : model_->bookmark_bar_node();
1059 int index = drop_info_->location.index;
1061 if (index != -1) {
1062 // TODO(sky): optimize the SchedulePaint region.
1063 SchedulePaint();
1065 const BookmarkNode* parent_node;
1066 if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
1067 parent_node = root;
1068 index = parent_node->child_count();
1069 } else if (drop_info_->location.on) {
1070 parent_node = root->GetChild(index);
1071 index = parent_node->child_count();
1072 } else {
1073 parent_node = root;
1075 const BookmarkNodeData data = drop_info_->data;
1076 DCHECK(data.is_valid());
1077 bool copy = drop_info_->location.operation == ui::DragDropTypes::DRAG_COPY;
1078 drop_info_.reset();
1079 return chrome::DropBookmarks(
1080 browser_->profile(), data, parent_node, index, copy);
1083 void BookmarkBarView::OnThemeChanged() {
1084 UpdateColors();
1087 const char* BookmarkBarView::GetClassName() const {
1088 return kViewClassName;
1091 void BookmarkBarView::SetVisible(bool v) {
1092 if (v == visible())
1093 return;
1095 View::SetVisible(v);
1096 FOR_EACH_OBSERVER(BookmarkBarViewObserver, observers_,
1097 OnBookmarkBarVisibilityChanged());
1100 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) {
1101 state->role = ui::AX_ROLE_TOOLBAR;
1102 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
1105 void BookmarkBarView::AnimationProgressed(const gfx::Animation* animation) {
1106 // |browser_view_| can be NULL during tests.
1107 if (browser_view_)
1108 browser_view_->ToolbarSizeChanged(true);
1111 void BookmarkBarView::AnimationEnded(const gfx::Animation* animation) {
1112 // |browser_view_| can be NULL during tests.
1113 if (browser_view_) {
1114 browser_view_->ToolbarSizeChanged(false);
1115 SchedulePaint();
1119 void BookmarkBarView::BookmarkMenuControllerDeleted(
1120 BookmarkMenuController* controller) {
1121 if (controller == bookmark_menu_)
1122 bookmark_menu_ = NULL;
1123 else if (controller == bookmark_drop_menu_)
1124 bookmark_drop_menu_ = NULL;
1127 void BookmarkBarView::ShowImportDialog() {
1128 int64 install_time = g_browser_process->metrics_service()->GetInstallDate();
1129 int64 time_from_install = base::Time::Now().ToTimeT() - install_time;
1130 if (bookmark_bar_state_ == BookmarkBar::SHOW) {
1131 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
1132 time_from_install);
1133 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) {
1134 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1135 time_from_install);
1138 chrome::ShowImportDialog(browser_);
1141 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) {
1142 StopThrobbing(true);
1143 const BookmarkNode* node = model_->GetMostRecentlyAddedUserNodeForURL(url);
1144 if (!node)
1145 return; // Generally shouldn't happen.
1146 StartThrobbing(node, false);
1149 void BookmarkBarView::OnBookmarkBubbleHidden() {
1150 StopThrobbing(false);
1153 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model,
1154 bool ids_reassigned) {
1155 // There should be no buttons. If non-zero it means Load was invoked more than
1156 // once, or we didn't properly clear things. Either of which shouldn't happen.
1157 DCHECK_EQ(0, GetBookmarkButtonCount());
1158 const BookmarkNode* node = model->bookmark_bar_node();
1159 DCHECK(node);
1160 // Create a button for each of the children on the bookmark bar.
1161 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1162 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1163 DCHECK(model->other_node());
1164 other_bookmarked_button_->SetAccessibleName(model->other_node()->GetTitle());
1165 other_bookmarked_button_->SetText(model->other_node()->GetTitle());
1166 managed_bookmarks_button_->SetAccessibleName(
1167 client_->managed_node()->GetTitle());
1168 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1169 UpdateColors();
1170 UpdateButtonsVisibility();
1171 other_bookmarked_button_->SetEnabled(true);
1172 managed_bookmarks_button_->SetEnabled(true);
1174 Layout();
1175 SchedulePaint();
1178 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
1179 NOTREACHED();
1180 // Do minimal cleanup, presumably we'll be deleted shortly.
1181 model_->RemoveObserver(this);
1182 model_ = NULL;
1185 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
1186 const BookmarkNode* old_parent,
1187 int old_index,
1188 const BookmarkNode* new_parent,
1189 int new_index) {
1190 bool was_throbbing = throbbing_view_ &&
1191 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
1192 if (was_throbbing)
1193 throbbing_view_->StopThrobbing();
1194 BookmarkNodeRemovedImpl(model, old_parent, old_index);
1195 BookmarkNodeAddedImpl(model, new_parent, new_index);
1196 if (was_throbbing)
1197 StartThrobbing(new_parent->GetChild(new_index), false);
1200 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
1201 const BookmarkNode* parent,
1202 int index) {
1203 BookmarkNodeAddedImpl(model, parent, index);
1206 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1207 const BookmarkNode* parent,
1208 int old_index,
1209 const BookmarkNode* node,
1210 const std::set<GURL>& removed_urls) {
1211 // Close the menu if the menu is showing for the deleted node.
1212 if (bookmark_menu_ && bookmark_menu_->node() == node)
1213 bookmark_menu_->Cancel();
1214 BookmarkNodeRemovedImpl(model, parent, old_index);
1217 void BookmarkBarView::BookmarkAllUserNodesRemoved(
1218 BookmarkModel* model,
1219 const std::set<GURL>& removed_urls) {
1220 UpdateButtonsVisibility();
1222 StopThrobbing(true);
1224 // Remove the existing buttons.
1225 while (GetBookmarkButtonCount()) {
1226 delete GetBookmarkButton(0);
1229 Layout();
1230 SchedulePaint();
1233 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1234 const BookmarkNode* node) {
1235 BookmarkNodeChangedImpl(model, node);
1238 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1239 const BookmarkNode* node) {
1240 if (node != model->bookmark_bar_node())
1241 return; // We only care about reordering of the bookmark bar node.
1243 // Remove the existing buttons.
1244 while (GetBookmarkButtonCount()) {
1245 views::View* button = child_at(0);
1246 RemoveChildView(button);
1247 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1250 // Create the new buttons.
1251 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1252 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1253 UpdateColors();
1255 Layout();
1256 SchedulePaint();
1259 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model,
1260 const BookmarkNode* node) {
1261 BookmarkNodeChangedImpl(model, node);
1264 void BookmarkBarView::WriteDragDataForView(View* sender,
1265 const gfx::Point& press_pt,
1266 ui::OSExchangeData* data) {
1267 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1269 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1270 if (sender == GetBookmarkButton(i)) {
1271 views::LabelButton* button = GetBookmarkButton(i);
1272 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1274 const gfx::Image& image_from_model = model_->GetFavicon(node);
1275 const gfx::ImageSkia& icon = image_from_model.IsEmpty() ?
1276 (node->is_folder() ? GetFolderIcon() : GetDefaultFavicon()) :
1277 *image_from_model.ToImageSkia();
1279 button_drag_utils::SetDragImage(
1280 node->url(),
1281 node->GetTitle(),
1282 icon,
1283 &press_pt,
1284 data,
1285 button->GetWidget());
1286 WriteBookmarkDragData(model_->bookmark_bar_node()->GetChild(i), data);
1287 return;
1290 NOTREACHED();
1293 int BookmarkBarView::GetDragOperationsForView(View* sender,
1294 const gfx::Point& p) {
1295 if (size_animation_->is_animating() ||
1296 (size_animation_->GetCurrentValue() == 0 &&
1297 bookmark_bar_state_ != BookmarkBar::DETACHED)) {
1298 // Don't let the user drag while animating open or we're closed (and not
1299 // detached, when detached size_animation_ is always 0). This typically is
1300 // only hit if the user does something to inadvertently trigger DnD such as
1301 // pressing the mouse and hitting control-b.
1302 return ui::DragDropTypes::DRAG_NONE;
1305 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1306 if (sender == GetBookmarkButton(i)) {
1307 return chrome::GetBookmarkDragOperation(
1308 browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
1311 NOTREACHED();
1312 return ui::DragDropTypes::DRAG_NONE;
1315 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1316 const gfx::Point& press_pt,
1317 const gfx::Point& p) {
1318 // Check if we have not moved enough horizontally but we have moved downward
1319 // vertically - downward drag.
1320 gfx::Vector2d move_offset = p - press_pt;
1321 gfx::Vector2d horizontal_offset(move_offset.x(), 0);
1322 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
1323 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1324 if (sender == GetBookmarkButton(i)) {
1325 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1326 // If the folder button was dragged, show the menu instead.
1327 if (node && node->is_folder()) {
1328 views::MenuButton* menu_button =
1329 static_cast<views::MenuButton*>(sender);
1330 menu_button->Activate();
1331 return false;
1333 break;
1337 return true;
1340 void BookmarkBarView::OnMenuButtonClicked(views::View* view,
1341 const gfx::Point& point) {
1342 const BookmarkNode* node;
1344 int start_index = 0;
1345 if (view == other_bookmarked_button_) {
1346 node = model_->other_node();
1347 } else if (view == managed_bookmarks_button_) {
1348 node = client_->managed_node();
1349 } else if (view == overflow_button_) {
1350 node = model_->bookmark_bar_node();
1351 start_index = GetFirstHiddenNodeIndex();
1352 } else {
1353 int button_index = GetIndexOf(view);
1354 DCHECK_NE(-1, button_index);
1355 node = model_->bookmark_bar_node()->GetChild(button_index);
1358 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1359 bookmark_menu_ = new BookmarkMenuController(
1360 browser_, page_navigator_, GetWidget(), node, start_index, false);
1361 bookmark_menu_->set_observer(this);
1362 bookmark_menu_->RunMenuAt(this);
1365 void BookmarkBarView::ButtonPressed(views::Button* sender,
1366 const ui::Event& event) {
1367 WindowOpenDisposition disposition_from_event_flags =
1368 ui::DispositionFromEventFlags(event.flags());
1370 if (sender->tag() == kAppsShortcutButtonTag) {
1371 OpenURLParams params(GURL(chrome::kChromeUIAppsURL),
1372 Referrer(),
1373 disposition_from_event_flags,
1374 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1375 false);
1376 page_navigator_->OpenURL(params);
1377 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1378 return;
1381 const BookmarkNode* node;
1382 if (sender->tag() == kOtherFolderButtonTag) {
1383 node = model_->other_node();
1384 } else if (sender->tag() == kManagedFolderButtonTag) {
1385 node = client_->managed_node();
1386 } else {
1387 int index = GetIndexOf(sender);
1388 DCHECK_NE(-1, index);
1389 node = model_->bookmark_bar_node()->GetChild(index);
1391 DCHECK(page_navigator_);
1393 if (node->is_url()) {
1394 RecordAppLaunch(browser_->profile(), node->url());
1395 OpenURLParams params(
1396 node->url(), Referrer(), disposition_from_event_flags,
1397 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
1398 page_navigator_->OpenURL(params);
1399 } else {
1400 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node,
1401 disposition_from_event_flags, browser_->profile());
1404 RecordBookmarkLaunch(node, GetBookmarkLaunchLocation());
1407 void BookmarkBarView::ShowContextMenuForView(views::View* source,
1408 const gfx::Point& point,
1409 ui::MenuSourceType source_type) {
1410 if (!model_->loaded()) {
1411 // Don't do anything if the model isn't loaded.
1412 return;
1415 const BookmarkNode* parent = NULL;
1416 std::vector<const BookmarkNode*> nodes;
1417 if (source == other_bookmarked_button_) {
1418 parent = model_->other_node();
1419 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1420 // sure the user can't edit/delete the node in this case.
1421 nodes.push_back(parent);
1422 } else if (source == managed_bookmarks_button_) {
1423 parent = client_->managed_node();
1424 nodes.push_back(parent);
1425 } else if (source != this && source != apps_page_shortcut_) {
1426 // User clicked on one of the bookmark buttons, find which one they
1427 // clicked on, except for the apps page shortcut, which must behave as if
1428 // the user clicked on the bookmark bar background.
1429 int bookmark_button_index = GetIndexOf(source);
1430 DCHECK(bookmark_button_index != -1 &&
1431 bookmark_button_index < GetBookmarkButtonCount());
1432 const BookmarkNode* node =
1433 model_->bookmark_bar_node()->GetChild(bookmark_button_index);
1434 nodes.push_back(node);
1435 parent = node->parent();
1436 } else {
1437 parent = model_->bookmark_bar_node();
1438 nodes.push_back(parent);
1440 bool close_on_remove =
1441 (parent == model_->other_node()) && (parent->child_count() == 1);
1443 context_menu_.reset(new BookmarkContextMenu(
1444 GetWidget(), browser_, browser_->profile(),
1445 browser_->tab_strip_model()->GetActiveWebContents(),
1446 parent, nodes, close_on_remove));
1447 context_menu_->RunMenuAt(point, source_type);
1450 void BookmarkBarView::Init() {
1451 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1452 // return NULL. When we're inserted into a hierarchy, we'll call
1453 // UpdateColors(), which will set the appropriate colors for all the objects
1454 // added in this function.
1456 // Child views are traversed in the order they are added. Make sure the order
1457 // they are added matches the visual order.
1458 overflow_button_ = CreateOverflowButton();
1459 AddChildView(overflow_button_);
1461 other_bookmarked_button_ = CreateOtherBookmarkedButton();
1462 // We'll re-enable when the model is loaded.
1463 other_bookmarked_button_->SetEnabled(false);
1464 AddChildView(other_bookmarked_button_);
1466 managed_bookmarks_button_ = CreateManagedBookmarksButton();
1467 // Also re-enabled when the model is loaded.
1468 managed_bookmarks_button_->SetEnabled(false);
1469 AddChildView(managed_bookmarks_button_);
1471 apps_page_shortcut_ = CreateAppsPageShortcutButton();
1472 AddChildView(apps_page_shortcut_);
1473 profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
1474 profile_pref_registrar_.Add(
1475 bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1476 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged,
1477 base::Unretained(this)));
1478 profile_pref_registrar_.Add(
1479 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
1480 base::Bind(&BookmarkBarView::UpdateButtonsVisibility,
1481 base::Unretained(this)));
1482 apps_page_shortcut_->SetVisible(
1483 chrome::ShouldShowAppsShortcutInBookmarkBar(
1484 browser_->profile(), browser_->host_desktop_type()));
1486 bookmarks_separator_view_ = new ButtonSeparatorView();
1487 AddChildView(bookmarks_separator_view_);
1488 UpdateBookmarksSeparatorVisibility();
1490 instructions_ = new BookmarkBarInstructionsView(this);
1491 AddChildView(instructions_);
1493 set_context_menu_controller(this);
1495 size_animation_.reset(new gfx::SlideAnimation(this));
1497 model_ = BookmarkModelFactory::GetForProfile(browser_->profile());
1498 client_ = ChromeBookmarkClientFactory::GetForProfile(browser_->profile());
1499 if (model_) {
1500 model_->AddObserver(this);
1501 if (model_->loaded())
1502 BookmarkModelLoaded(model_, false);
1503 // else case: we'll receive notification back from the BookmarkModel when
1504 // done loading, then we'll populate the bar.
1508 int BookmarkBarView::GetBookmarkButtonCount() const {
1509 // We contain six non-bookmark button views: managed bookmarks,
1510 // other bookmarks, bookmarks separator, chevrons (for overflow), apps page,
1511 // and the instruction label.
1512 return child_count() - 6;
1515 views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) {
1516 DCHECK(index >= 0 && index < GetBookmarkButtonCount());
1517 return static_cast<views::LabelButton*>(child_at(index));
1520 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
1521 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
1522 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
1525 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1526 const int bb_count = GetBookmarkButtonCount();
1527 for (int i = 0; i < bb_count; ++i) {
1528 if (!GetBookmarkButton(i)->visible())
1529 return i;
1531 return bb_count;
1534 MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() {
1535 // Title is set in Loaded.
1536 MenuButton* button =
1537 new BookmarkFolderButton(this, base::string16(), this, false);
1538 button->set_id(VIEW_ID_OTHER_BOOKMARKS);
1539 button->SetImage(views::Button::STATE_NORMAL, GetFolderIcon());
1540 button->set_context_menu_controller(this);
1541 button->set_tag(kOtherFolderButtonTag);
1542 return button;
1545 MenuButton* BookmarkBarView::CreateManagedBookmarksButton() {
1546 // Title is set in Loaded.
1547 MenuButton* button =
1548 new BookmarkFolderButton(this, base::string16(), this, false);
1549 button->set_id(VIEW_ID_MANAGED_BOOKMARKS);
1550 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1551 gfx::ImageSkia* image =
1552 rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER_MANAGED);
1553 button->SetImage(views::Button::STATE_NORMAL, *image);
1554 button->set_context_menu_controller(this);
1555 button->set_tag(kManagedFolderButtonTag);
1556 return button;
1559 MenuButton* BookmarkBarView::CreateOverflowButton() {
1560 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1561 MenuButton* button = new OverFlowButton(this);
1562 button->SetImage(views::Button::STATE_NORMAL,
1563 *rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS));
1565 // The overflow button's image contains an arrow and therefore it is a
1566 // direction sensitive image and we need to flip it if the UI layout is
1567 // right-to-left.
1569 // By default, menu buttons are not flipped because they generally contain
1570 // text and flipping the gfx::Canvas object will break text rendering. Since
1571 // the overflow button does not contain text, we can safely flip it.
1572 button->EnableCanvasFlippingForRTLUI(true);
1574 // Make visible as necessary.
1575 button->SetVisible(false);
1576 // Set accessibility name.
1577 button->SetAccessibleName(
1578 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
1579 return button;
1582 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1583 if (node->is_url()) {
1584 BookmarkButton* button = new BookmarkButton(
1585 this, node->url(), node->GetTitle(), browser_->profile());
1586 ConfigureButton(node, button);
1587 return button;
1588 } else {
1589 views::MenuButton* button = new BookmarkFolderButton(
1590 this, node->GetTitle(), this, false);
1591 button->SetImage(views::Button::STATE_NORMAL, GetFolderIcon());
1592 ConfigureButton(node, button);
1593 return button;
1597 views::LabelButton* BookmarkBarView::CreateAppsPageShortcutButton() {
1598 views::LabelButton* button = new ShortcutButton(
1599 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME));
1600 button->SetTooltipText(l10n_util::GetStringUTF16(
1601 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP));
1602 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1603 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1604 button->SetImage(views::Button::STATE_NORMAL,
1605 *rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT));
1606 button->set_context_menu_controller(this);
1607 button->set_tag(kAppsShortcutButtonTag);
1608 return button;
1611 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1612 views::LabelButton* button) {
1613 button->SetText(node->GetTitle());
1614 button->SetAccessibleName(node->GetTitle());
1615 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1616 // We don't always have a theme provider (ui tests, for example).
1617 if (GetThemeProvider()) {
1618 button->SetTextColor(
1619 views::Button::STATE_NORMAL,
1620 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
1623 button->SetMinSize(gfx::Size());
1624 button->set_context_menu_controller(this);
1625 button->set_drag_controller(this);
1626 if (node->is_url()) {
1627 const gfx::Image& favicon = model_->GetFavicon(node);
1628 if (!favicon.IsEmpty())
1629 button->SetImage(views::Button::STATE_NORMAL, *favicon.ToImageSkia());
1630 else
1631 button->SetImage(views::Button::STATE_NORMAL, GetDefaultFavicon());
1633 button->SetMaxSize(gfx::Size(kMaxButtonWidth, 0));
1636 void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
1637 const BookmarkNode* parent,
1638 int index) {
1639 UpdateButtonsVisibility();
1640 if (parent != model->bookmark_bar_node()) {
1641 // We only care about nodes on the bookmark bar.
1642 return;
1644 DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
1645 const BookmarkNode* node = parent->GetChild(index);
1646 ProfileSyncService* sync_service(ProfileSyncServiceFactory::
1647 GetInstance()->GetForProfile(browser_->profile()));
1648 if (!throbbing_view_ && sync_service && sync_service->FirstSetupInProgress())
1649 StartThrobbing(node, true);
1650 AddChildViewAt(CreateBookmarkButton(node), index);
1651 UpdateColors();
1652 Layout();
1653 SchedulePaint();
1656 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1657 const BookmarkNode* parent,
1658 int index) {
1659 UpdateButtonsVisibility();
1661 StopThrobbing(true);
1662 // No need to start throbbing again as the bookmark bubble can't be up at
1663 // the same time as the user reorders.
1665 if (parent != model->bookmark_bar_node()) {
1666 // We only care about nodes on the bookmark bar.
1667 return;
1669 DCHECK(index >= 0 && index < GetBookmarkButtonCount());
1670 views::View* button = child_at(index);
1671 RemoveChildView(button);
1672 base::MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1673 Layout();
1674 SchedulePaint();
1677 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1678 const BookmarkNode* node) {
1679 if (node == client_->managed_node()) {
1680 // The managed node may have its title updated.
1681 managed_bookmarks_button_->SetAccessibleName(
1682 client_->managed_node()->GetTitle());
1683 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1684 return;
1687 if (node->parent() != model->bookmark_bar_node()) {
1688 // We only care about nodes on the bookmark bar.
1689 return;
1691 int index = model->bookmark_bar_node()->GetIndexOf(node);
1692 DCHECK_NE(-1, index);
1693 views::LabelButton* button = GetBookmarkButton(index);
1694 gfx::Size old_pref = button->GetPreferredSize();
1695 ConfigureButton(node, button);
1696 gfx::Size new_pref = button->GetPreferredSize();
1697 if (old_pref.width() != new_pref.width()) {
1698 Layout();
1699 SchedulePaint();
1700 } else if (button->visible()) {
1701 button->SchedulePaint();
1705 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1706 if (bookmark_drop_menu_) {
1707 if (bookmark_drop_menu_->node() == node) {
1708 // Already showing for the specified node.
1709 return;
1711 bookmark_drop_menu_->Cancel();
1714 views::MenuButton* menu_button = GetMenuButtonForNode(node);
1715 if (!menu_button)
1716 return;
1718 int start_index = 0;
1719 if (node == model_->bookmark_bar_node())
1720 start_index = GetFirstHiddenNodeIndex();
1722 drop_info_->is_menu_showing = true;
1723 bookmark_drop_menu_ = new BookmarkMenuController(
1724 browser_, page_navigator_, GetWidget(), node, start_index, true);
1725 bookmark_drop_menu_->set_observer(this);
1726 bookmark_drop_menu_->RunMenuAt(this);
1729 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1730 show_folder_method_factory_.InvalidateWeakPtrs();
1733 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1734 if (!animations_enabled) {
1735 // So that tests can run as fast as possible disable the delay during
1736 // testing.
1737 ShowDropFolderForNode(node);
1738 return;
1740 show_folder_method_factory_.InvalidateWeakPtrs();
1741 base::MessageLoop::current()->PostDelayedTask(
1742 FROM_HERE,
1743 base::Bind(&BookmarkBarView::ShowDropFolderForNode,
1744 show_folder_method_factory_.GetWeakPtr(),
1745 node),
1746 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1749 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event,
1750 const BookmarkNodeData& data,
1751 DropLocation* location) {
1752 DCHECK(model_);
1753 DCHECK(model_->loaded());
1754 DCHECK(data.is_valid());
1756 *location = DropLocation();
1758 // The drop event uses the screen coordinates while the child Views are
1759 // always laid out from left to right (even though they are rendered from
1760 // right-to-left on RTL locales). Thus, in order to make sure the drop
1761 // coordinates calculation works, we mirror the event's X coordinate if the
1762 // locale is RTL.
1763 int mirrored_x = GetMirroredXInView(event.x());
1765 bool found = false;
1766 const int other_delta_x = mirrored_x - other_bookmarked_button_->x();
1767 Profile* profile = browser_->profile();
1768 if (other_bookmarked_button_->visible() && other_delta_x >= 0 &&
1769 other_delta_x < other_bookmarked_button_->width()) {
1770 // Mouse is over 'other' folder.
1771 location->button_type = DROP_OTHER_FOLDER;
1772 location->on = true;
1773 found = true;
1774 } else if (!GetBookmarkButtonCount()) {
1775 // No bookmarks, accept the drop.
1776 location->index = 0;
1777 const BookmarkNode* node = data.GetFirstNode(model_, profile->GetPath());
1778 int ops = node && client_->CanBeEditedByUser(node) ?
1779 ui::DragDropTypes::DRAG_MOVE :
1780 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
1781 location->operation = chrome::GetPreferredBookmarkDropOperation(
1782 event.source_operations(), ops);
1783 return;
1786 for (int i = 0; i < GetBookmarkButtonCount() &&
1787 GetBookmarkButton(i)->visible() && !found; i++) {
1788 views::LabelButton* button = GetBookmarkButton(i);
1789 int button_x = mirrored_x - button->x();
1790 int button_w = button->width();
1791 if (button_x < button_w) {
1792 found = true;
1793 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1794 if (node->is_folder()) {
1795 if (button_x <= views::kDropBetweenPixels) {
1796 location->index = i;
1797 } else if (button_x < button_w - views::kDropBetweenPixels) {
1798 location->index = i;
1799 location->on = true;
1800 } else {
1801 location->index = i + 1;
1803 } else if (button_x < button_w / 2) {
1804 location->index = i;
1805 } else {
1806 location->index = i + 1;
1808 break;
1812 if (!found) {
1813 if (overflow_button_->visible()) {
1814 // Are we over the overflow button?
1815 int overflow_delta_x = mirrored_x - overflow_button_->x();
1816 if (overflow_delta_x >= 0 &&
1817 overflow_delta_x < overflow_button_->width()) {
1818 // Mouse is over overflow button.
1819 location->index = GetFirstHiddenNodeIndex();
1820 location->button_type = DROP_OVERFLOW;
1821 } else if (overflow_delta_x < 0) {
1822 // Mouse is after the last visible button but before overflow button;
1823 // use the last visible index.
1824 location->index = GetFirstHiddenNodeIndex();
1825 } else {
1826 return;
1828 } else if (!other_bookmarked_button_->visible() ||
1829 mirrored_x < other_bookmarked_button_->x()) {
1830 // Mouse is after the last visible button but before more recently
1831 // bookmarked; use the last visible index.
1832 location->index = GetFirstHiddenNodeIndex();
1833 } else {
1834 return;
1838 if (location->on) {
1839 const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ?
1840 model_->other_node() :
1841 model_->bookmark_bar_node()->GetChild(location->index);
1842 location->operation = chrome::GetBookmarkDropOperation(
1843 profile, event, data, parent, parent->child_count());
1844 if (!location->operation && !data.has_single_url() &&
1845 data.GetFirstNode(model_, profile->GetPath()) == parent) {
1846 // Don't open a menu if the node being dragged is the menu to open.
1847 location->on = false;
1849 } else {
1850 location->operation = chrome::GetBookmarkDropOperation(
1851 profile, event, data, model_->bookmark_bar_node(), location->index);
1855 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
1856 ui::OSExchangeData* data) {
1857 DCHECK(node && data);
1858 BookmarkNodeData drag_data(node);
1859 drag_data.Write(browser_->profile()->GetPath(), data);
1862 void BookmarkBarView::StartThrobbing(const BookmarkNode* node,
1863 bool overflow_only) {
1864 DCHECK(!throbbing_view_);
1866 // Determine which visible button is showing the bookmark (or is an ancestor
1867 // of the bookmark).
1868 const BookmarkNode* bbn = model_->bookmark_bar_node();
1869 const BookmarkNode* parent_on_bb = node;
1870 while (parent_on_bb) {
1871 const BookmarkNode* parent = parent_on_bb->parent();
1872 if (parent == bbn)
1873 break;
1874 parent_on_bb = parent;
1876 if (parent_on_bb) {
1877 int index = bbn->GetIndexOf(parent_on_bb);
1878 if (index >= GetFirstHiddenNodeIndex()) {
1879 // Node is hidden, animate the overflow button.
1880 throbbing_view_ = overflow_button_;
1881 } else if (!overflow_only) {
1882 throbbing_view_ = static_cast<CustomButton*>(child_at(index));
1884 } else if (client_->IsDescendantOfManagedNode(node)) {
1885 throbbing_view_ = managed_bookmarks_button_;
1886 } else if (!overflow_only) {
1887 throbbing_view_ = other_bookmarked_button_;
1890 // Use a large number so that the button continues to throb.
1891 if (throbbing_view_)
1892 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
1895 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove(
1896 const BookmarkNode* parent,
1897 int old_index) {
1898 const BookmarkNode* bbn = model_->bookmark_bar_node();
1899 const BookmarkNode* old_node = parent;
1900 int old_index_on_bb = old_index;
1901 while (old_node && old_node != bbn) {
1902 const BookmarkNode* parent = old_node->parent();
1903 if (parent == bbn) {
1904 old_index_on_bb = bbn->GetIndexOf(old_node);
1905 break;
1907 old_node = parent;
1909 if (old_node) {
1910 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) {
1911 // Node is hidden, animate the overflow button.
1912 return overflow_button_;
1914 return static_cast<CustomButton*>(child_at(old_index_on_bb));
1916 if (client_->IsDescendantOfManagedNode(parent))
1917 return managed_bookmarks_button_;
1918 // Node wasn't on the bookmark bar, use the other bookmark button.
1919 return other_bookmarked_button_;
1922 void BookmarkBarView::UpdateColors() {
1923 // We don't always have a theme provider (ui tests, for example).
1924 const ui::ThemeProvider* theme_provider = GetThemeProvider();
1925 if (!theme_provider)
1926 return;
1927 SkColor color =
1928 theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
1929 for (int i = 0; i < GetBookmarkButtonCount(); ++i)
1930 GetBookmarkButton(i)->SetTextColor(views::Button::STATE_NORMAL, color);
1931 other_bookmarked_button_->SetTextColor(views::Button::STATE_NORMAL, color);
1932 managed_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color);
1933 if (apps_page_shortcut_->visible())
1934 apps_page_shortcut_->SetTextColor(views::Button::STATE_NORMAL, color);
1937 void BookmarkBarView::UpdateButtonsVisibility() {
1938 bool has_other_children = !model_->other_node()->empty();
1939 bool update_other = has_other_children != other_bookmarked_button_->visible();
1940 if (update_other) {
1941 other_bookmarked_button_->SetVisible(has_other_children);
1942 UpdateBookmarksSeparatorVisibility();
1945 bool show_managed = !client_->managed_node()->empty() &&
1946 browser_->profile()->GetPrefs()->GetBoolean(
1947 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
1948 bool update_managed = show_managed != managed_bookmarks_button_->visible();
1949 if (update_managed)
1950 managed_bookmarks_button_->SetVisible(show_managed);
1952 if (update_other || update_managed) {
1953 Layout();
1954 SchedulePaint();
1958 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() {
1959 // Ash does not paint the bookmarks separator line because it looks odd on
1960 // the flat background. We keep it present for layout, but don't draw it.
1961 bookmarks_separator_view_->SetVisible(
1962 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH &&
1963 other_bookmarked_button_->visible());
1966 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() {
1967 DCHECK(apps_page_shortcut_);
1968 // Only perform layout if required.
1969 bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar(
1970 browser_->profile(), browser_->host_desktop_type());
1971 if (apps_page_shortcut_->visible() == visible)
1972 return;
1973 apps_page_shortcut_->SetVisible(visible);
1974 UpdateBookmarksSeparatorVisibility();
1975 Layout();