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"
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
;
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;
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;
155 static const gfx::ElideBehavior kElideBehavior
= gfx::FADE_TAIL
;
157 // Windows fade eliding causes text to darken; see http://crbug.com/388084
158 static const gfx::ElideBehavior kElideBehavior
= gfx::ELIDE_TAIL
;
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
{
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);
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
);
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
{
218 // The internal view class name.
219 static const char kViewClassName
[];
221 BookmarkButton(views::ButtonListener
* listener
,
223 const base::string16
& title
,
225 : BookmarkButtonBase(listener
, title
),
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
;
247 DISALLOW_COPY_AND_ASSIGN(BookmarkButton
);
251 const char BookmarkButton::kViewClassName
[] = "BookmarkButton";
253 // ShortcutButton -------------------------------------------------------------
255 // Buttons used for the shortcuts on the bookmark bar.
257 class ShortcutButton
: public BookmarkButtonBase
{
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
;
272 DISALLOW_COPY_AND_ASSIGN(ShortcutButton
);
276 const char ShortcutButton::kViewClassName
[] = "ShortcutButton";
278 // BookmarkFolderButton -------------------------------------------------------
280 // Buttons used for folders on the bookmark bar, including the 'other folders'
282 class BookmarkFolderButton
: public views::MenuButton
{
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);
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
311 if (e
.type() == ui::ET_GESTURE_TAP
||
312 (e
.IsMouseEvent() && (e
.flags() &
313 (ui::EF_LEFT_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
))))
316 if (e
.IsMouseEvent())
317 return ui::DispositionFromEventFlags(e
.flags()) != CURRENT_TAB
;
322 scoped_ptr
<gfx::SlideAnimation
> show_animation_
;
324 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton
);
327 // OverFlowButton (chevron) --------------------------------------------------
329 class OverFlowButton
: public views::MenuButton
{
331 explicit OverFlowButton(BookmarkBarView
* owner
)
332 : MenuButton(NULL
, base::string16(), owner
, false),
335 virtual bool OnMousePressed(const ui::MouseEvent
& e
) override
{
336 owner_
->StopThrobbing(true);
337 return views::MenuButton::OnMousePressed(e
);
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
);
353 CoreAppLauncherHandler::RecordAppLaunchType(
354 extension_misc::APP_LAUNCH_BOOKMARK_BAR
,
355 extension
->GetType());
360 // DropLocation ---------------------------------------------------------------
362 struct BookmarkBarView::DropLocation
{
365 operation(ui::DragDropTypes::DRAG_NONE
),
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.
381 // If true, the user is dropping on a folder.
385 DropButtonType button_type
;
388 // DropInfo -------------------------------------------------------------------
390 // Tracks drops on the BookmarkBarView.
392 struct BookmarkBarView::DropInfo
{
395 is_menu_showing(false),
400 // Whether the data is valid.
403 // If true, the menu is being shown.
404 bool is_menu_showing
;
406 // Coordinates of the drag (in terms of the BookmarkBarView).
410 // DropData for the drop.
411 BookmarkNodeData data
;
413 DropLocation location
;
416 // ButtonSeparatorView --------------------------------------------------------
418 class BookmarkBarView::ButtonSeparatorView
: public views::View
{
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
;
443 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView
);
446 // BookmarkBarView ------------------------------------------------------------
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() {
463 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
464 kFolderIcon
= rb
->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER
);
469 BookmarkBarView::BookmarkBarView(Browser
* browser
, BrowserView
* browser_view
)
470 : page_navigator_(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
),
479 bookmarks_separator_view_(NULL
),
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
);
490 size_animation_
->Reset(1);
493 BookmarkBarView::~BookmarkBarView() {
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();
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
;
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();
541 size_animation_
->Hide();
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
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())
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())
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();
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())
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
;
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_
)
641 // If not immediate, cycle through 2 more complete cycles.
642 throbbing_view_
->StartThrobbing(immediate
? 0 : 4);
643 throbbing_view_
= NULL
;
647 base::string16
BookmarkBarView::CreateToolTipForURLAndTitle(
648 const views::Widget
* widget
,
649 const gfx::Point
& screen_loc
,
651 const base::string16
& title
,
653 int max_width
= views::TooltipManager::GetMaxWidth(
656 widget
->GetNativeView());
657 const gfx::FontList tt_fonts
= widget
->GetTooltipManager()->GetFontList();
658 base::string16 result
;
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
,
668 // Only show the URL if the url and title differ.
669 if (title
!= base::UTF8ToUTF16(url
.spec())) {
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
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
);
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
;
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
712 return detached_overlap
+ static_cast<int>(
713 (attached_overlap
- detached_overlap
) *
714 size_animation_
->GetCurrentValue());
717 gfx::Size
BookmarkBarView::GetPreferredSize() const {
721 chrome::kBookmarkBarHeight
+
723 (chrome::kNTPBookmarkBarHeight
- chrome::kBookmarkBarHeight
) *
724 (1 - size_animation_
->GetCurrentValue())));
726 prefsize
.set_height(static_cast<int>(chrome::kBookmarkBarHeight
*
727 size_animation_
->GetCurrentValue()));
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()) {
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
;
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
) *
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() {
787 int top_margin
= IsDetached() ? kDetachedTopMargin
: 0;
789 int width
= View::width() - kRightMargin
- kLeftMargin
;
790 int height
= chrome::kBookmarkBarHeight
- kBottomMargin
;
791 int separator_margin
= kSeparatorMargin
;
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
);
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(),
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(),
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()),
846 instructions_
->SetVisible(true);
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
);
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();
873 if (bookmarks_separator_view_
->visible()) {
874 bookmarks_separator_view_
->SetBounds(x
,
876 bookmarks_separator_pref
.width(),
877 height
+ top_margin
+ kBottomMargin
-
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(),
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.
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.
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());
921 if (index
== GetBookmarkButtonCount()) {
925 x
= GetBookmarkButton(index
- 1)->x() +
926 GetBookmarkButton(index
- 1)->width();
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,
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(
951 std::set
<ui::OSExchangeData::CustomFormat
>* custom_formats
) {
952 if (!model_
|| !model_
->loaded())
954 *formats
= ui::OSExchangeData::URL
;
955 custom_formats
->insert(BookmarkNodeData::GetBookmarkCustomFormat());
959 bool BookmarkBarView::AreDropTypesRequired() {
963 bool BookmarkBarView::CanDrop(const ui::OSExchangeData
& data
) {
964 if (!model_
|| !model_
->loaded() ||
965 !browser_
->profile()->GetPrefs()->GetBoolean(
966 bookmarks::prefs::kEditBookmarksEnabled
))
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())
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.
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();
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.
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
;
1062 // TODO(sky): optimize the SchedulePaint region.
1065 const BookmarkNode
* parent_node
;
1066 if (drop_info_
->location
.button_type
== DROP_OTHER_FOLDER
) {
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();
1075 const BookmarkNodeData data
= drop_info_
->data
;
1076 DCHECK(data
.is_valid());
1077 bool copy
= drop_info_
->location
.operation
== ui::DragDropTypes::DRAG_COPY
;
1079 return chrome::DropBookmarks(
1080 browser_
->profile(), data
, parent_node
, index
, copy
);
1083 void BookmarkBarView::OnThemeChanged() {
1087 const char* BookmarkBarView::GetClassName() const {
1088 return kViewClassName
;
1091 void BookmarkBarView::SetVisible(bool v
) {
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.
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);
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",
1133 } else if (bookmark_bar_state_
== BookmarkBar::DETACHED
) {
1134 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1138 chrome::ShowImportDialog(browser_
);
1141 void BookmarkBarView::OnBookmarkBubbleShown(const GURL
& url
) {
1142 StopThrobbing(true);
1143 const BookmarkNode
* node
= model_
->GetMostRecentlyAddedUserNodeForURL(url
);
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();
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());
1170 UpdateButtonsVisibility();
1171 other_bookmarked_button_
->SetEnabled(true);
1172 managed_bookmarks_button_
->SetEnabled(true);
1178 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel
* model
) {
1180 // Do minimal cleanup, presumably we'll be deleted shortly.
1181 model_
->RemoveObserver(this);
1185 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel
* model
,
1186 const BookmarkNode
* old_parent
,
1188 const BookmarkNode
* new_parent
,
1190 bool was_throbbing
= throbbing_view_
&&
1191 throbbing_view_
== DetermineViewToThrobFromRemove(old_parent
, old_index
);
1193 throbbing_view_
->StopThrobbing();
1194 BookmarkNodeRemovedImpl(model
, old_parent
, old_index
);
1195 BookmarkNodeAddedImpl(model
, new_parent
, new_index
);
1197 StartThrobbing(new_parent
->GetChild(new_index
), false);
1200 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel
* model
,
1201 const BookmarkNode
* parent
,
1203 BookmarkNodeAddedImpl(model
, parent
, index
);
1206 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel
* model
,
1207 const BookmarkNode
* parent
,
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);
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
);
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(
1285 button
->GetWidget());
1286 WriteBookmarkDragData(model_
->bookmark_bar_node()->GetChild(i
), data
);
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
));
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();
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();
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
),
1373 disposition_from_event_flags
,
1374 ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1376 page_navigator_
->OpenURL(params
);
1377 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
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();
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
);
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.
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();
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());
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())
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
);
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
);
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
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
));
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
);
1589 views::MenuButton
* button
= new BookmarkFolderButton(
1590 this, node
->GetTitle(), this, false);
1591 button
->SetImage(views::Button::STATE_NORMAL
, GetFolderIcon());
1592 ConfigureButton(node
, 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
);
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());
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
,
1639 UpdateButtonsVisibility();
1640 if (parent
!= model
->bookmark_bar_node()) {
1641 // We only care about nodes on the bookmark bar.
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
);
1656 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel
* model
,
1657 const BookmarkNode
* parent
,
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.
1669 DCHECK(index
>= 0 && index
< GetBookmarkButtonCount());
1670 views::View
* button
= child_at(index
);
1671 RemoveChildView(button
);
1672 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, button
);
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());
1687 if (node
->parent() != model
->bookmark_bar_node()) {
1688 // We only care about nodes on the bookmark bar.
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()) {
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.
1711 bookmark_drop_menu_
->Cancel();
1714 views::MenuButton
* menu_button
= GetMenuButtonForNode(node
);
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
1737 ShowDropFolderForNode(node
);
1740 show_folder_method_factory_
.InvalidateWeakPtrs();
1741 base::MessageLoop::current()->PostDelayedTask(
1743 base::Bind(&BookmarkBarView::ShowDropFolderForNode
,
1744 show_folder_method_factory_
.GetWeakPtr(),
1746 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1749 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent
& event
,
1750 const BookmarkNodeData
& data
,
1751 DropLocation
* location
) {
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
1763 int mirrored_x
= GetMirroredXInView(event
.x());
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;
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
);
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
) {
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;
1801 location
->index
= i
+ 1;
1803 } else if (button_x
< button_w
/ 2) {
1804 location
->index
= i
;
1806 location
->index
= i
+ 1;
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();
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();
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;
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();
1874 parent_on_bb
= parent
;
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
,
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
);
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
)
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();
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();
1950 managed_bookmarks_button_
->SetVisible(show_managed
);
1952 if (update_other
|| update_managed
) {
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
)
1973 apps_page_shortcut_
->SetVisible(visible
);
1974 UpdateBookmarksSeparatorVisibility();