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/location.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
22 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/defaults.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/search/search.h"
28 #include "chrome/browser/sync/profile_sync_service.h"
29 #include "chrome/browser/sync/profile_sync_service_factory.h"
30 #include "chrome/browser/themes/theme_properties.h"
31 #include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
32 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
33 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
34 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
35 #include "chrome/browser/ui/browser.h"
36 #include "chrome/browser/ui/chrome_pages.h"
37 #include "chrome/browser/ui/tabs/tab_strip_model.h"
38 #include "chrome/browser/ui/view_ids.h"
39 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h"
40 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h"
41 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
42 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
43 #include "chrome/browser/ui/views/event_utils.h"
44 #include "chrome/browser/ui/views/frame/browser_view.h"
45 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
46 #include "chrome/common/chrome_switches.h"
47 #include "chrome/common/extensions/extension_constants.h"
48 #include "chrome/common/extensions/extension_metrics.h"
49 #include "chrome/common/pref_names.h"
50 #include "chrome/common/url_constants.h"
51 #include "chrome/grit/generated_resources.h"
52 #include "components/bookmarks/browser/bookmark_model.h"
53 #include "components/bookmarks/browser/bookmark_utils.h"
54 #include "components/bookmarks/managed/managed_bookmark_service.h"
55 #include "components/metrics/metrics_service.h"
56 #include "components/omnibox/browser/omnibox_popup_model.h"
57 #include "components/omnibox/browser/omnibox_view.h"
58 #include "components/url_formatter/elide_url.h"
59 #include "content/public/browser/notification_details.h"
60 #include "content/public/browser/notification_source.h"
61 #include "content/public/browser/page_navigator.h"
62 #include "content/public/browser/render_view_host.h"
63 #include "content/public/browser/render_widget_host_view.h"
64 #include "content/public/browser/user_metrics.h"
65 #include "content/public/browser/web_contents.h"
66 #include "extensions/browser/extension_registry.h"
67 #include "extensions/common/extension.h"
68 #include "extensions/common/extension_set.h"
69 #include "grit/theme_resources.h"
70 #include "ui/accessibility/ax_view_state.h"
71 #include "ui/base/dragdrop/drag_utils.h"
72 #include "ui/base/dragdrop/os_exchange_data.h"
73 #include "ui/base/l10n/l10n_util.h"
74 #include "ui/base/page_transition_types.h"
75 #include "ui/base/resource/resource_bundle.h"
76 #include "ui/base/theme_provider.h"
77 #include "ui/base/window_open_disposition.h"
78 #include "ui/compositor/paint_recorder.h"
79 #include "ui/gfx/animation/slide_animation.h"
80 #include "ui/gfx/canvas.h"
81 #include "ui/gfx/text_constants.h"
82 #include "ui/gfx/text_elider.h"
83 #include "ui/resources/grit/ui_resources.h"
84 #include "ui/views/button_drag_utils.h"
85 #include "ui/views/controls/button/label_button.h"
86 #include "ui/views/controls/button/label_button_border.h"
87 #include "ui/views/controls/button/menu_button.h"
88 #include "ui/views/controls/label.h"
89 #include "ui/views/drag_utils.h"
90 #include "ui/views/metrics.h"
91 #include "ui/views/view_constants.h"
92 #include "ui/views/widget/tooltip_manager.h"
93 #include "ui/views/widget/widget.h"
94 #include "ui/views/window/non_client_view.h"
96 using base::UserMetricsAction
;
97 using bookmarks::BookmarkModel
;
98 using bookmarks::BookmarkNode
;
99 using bookmarks::BookmarkNodeData
;
100 using content::OpenURLParams
;
101 using content::PageNavigator
;
102 using content::Referrer
;
103 using ui::DropTargetEvent
;
104 using views::CustomButton
;
105 using views::LabelButtonBorder
;
106 using views::MenuButton
;
109 // How inset the bookmarks bar is when displayed on the new tab page.
110 static const int kNewTabHorizontalPadding
= 2;
112 // Maximum size of buttons on the bookmark bar.
113 static const int kMaxButtonWidth
= 150;
115 // The color gradient start value close to the edge of the divider.
116 static const SkColor kEdgeDividerColor
= SkColorSetRGB(222, 234, 248);
118 // The color gradient value for the middle of the divider.
119 static const SkColor kMiddleDividerColor
= SkColorSetRGB(194, 205, 212);
121 // Number of pixels the attached bookmark bar overlaps with the toolbar.
122 static const int kToolbarAttachedBookmarkBarOverlap
= 3;
124 // Margins around the content.
125 static const int kDetachedTopMargin
= 1; // When attached, we use 0 and let the
126 // toolbar above serve as the margin.
127 static const int kBottomMargin
= 2;
128 static const int kLeftMargin
= 1;
129 static const int kRightMargin
= 1;
131 // Padding between buttons.
132 static const int kButtonPadding
= 0;
134 // Color of the drop indicator.
135 static const SkColor kDropIndicatorColor
= SK_ColorBLACK
;
137 // Width of the drop indicator.
138 static const int kDropIndicatorWidth
= 2;
140 // Distance between the bottom of the bar and the separator.
141 static const int kSeparatorMargin
= 1;
143 // Width of the separator between the recently bookmarked button and the
144 // overflow indicator.
145 static const int kSeparatorWidth
= 4;
147 // Starting x-coordinate of the separator line within a separator.
148 static const int kSeparatorStartX
= 2;
150 // Left-padding for the instructional text.
151 static const int kInstructionsPadding
= 6;
153 // Tag for the 'Other bookmarks' button.
154 static const int kOtherFolderButtonTag
= 1;
156 // Tag for the 'Apps Shortcut' button.
157 static const int kAppsShortcutButtonTag
= 2;
159 // Preferred padding between text and edge.
160 static const int kButtonPaddingHorizontal
= 6;
161 static const int kButtonPaddingVertical
= 4;
163 // Tag for the 'Managed bookmarks' button.
164 static const int kManagedFolderButtonTag
= 3;
165 // Tag for the 'Supervised bookmarks' button.
166 static const int kSupervisedFolderButtonTag
= 4;
169 static const gfx::ElideBehavior kElideBehavior
= gfx::FADE_TAIL
;
171 // Windows fade eliding causes text to darken; see http://crbug.com/388084
172 static const gfx::ElideBehavior kElideBehavior
= gfx::ELIDE_TAIL
;
177 // To enable/disable BookmarkBar animations during testing. In production
178 // animations are enabled by default.
179 bool animations_enabled
= true;
181 gfx::ImageSkia
* GetImageSkiaNamed(int id
) {
182 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id
);
185 // BookmarkButtonBase -----------------------------------------------
187 // Base class for buttons used on the bookmark bar.
189 class BookmarkButtonBase
: public views::LabelButton
{
191 BookmarkButtonBase(views::ButtonListener
* listener
,
192 const base::string16
& title
)
193 : LabelButton(listener
, title
) {
194 SetElideBehavior(kElideBehavior
);
195 show_animation_
.reset(new gfx::SlideAnimation(this));
196 if (!animations_enabled
) {
197 // For some reason during testing the events generated by animating
198 // throw off the test. So, don't animate while testing.
199 show_animation_
->Reset(1);
201 show_animation_
->Show();
205 View
* GetTooltipHandlerForPoint(const gfx::Point
& point
) override
{
206 return HitTestPoint(point
) && CanProcessEventsWithinSubtree() ? this : NULL
;
209 scoped_ptr
<LabelButtonBorder
> CreateDefaultBorder() const override
{
210 scoped_ptr
<LabelButtonBorder
> border
= LabelButton::CreateDefaultBorder();
211 border
->set_insets(gfx::Insets(kButtonPaddingVertical
,
212 kButtonPaddingHorizontal
,
213 kButtonPaddingVertical
,
214 kButtonPaddingHorizontal
));
215 return border
.Pass();
218 bool IsTriggerableEvent(const ui::Event
& e
) override
{
219 return e
.type() == ui::ET_GESTURE_TAP
||
220 e
.type() == ui::ET_GESTURE_TAP_DOWN
||
221 event_utils::IsPossibleDispositionEvent(e
);
225 scoped_ptr
<gfx::SlideAnimation
> show_animation_
;
227 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase
);
230 // BookmarkButton -------------------------------------------------------------
232 // Buttons used for the bookmarks on the bookmark bar.
234 class BookmarkButton
: public BookmarkButtonBase
{
236 // The internal view class name.
237 static const char kViewClassName
[];
239 BookmarkButton(views::ButtonListener
* listener
,
241 const base::string16
& title
,
243 : BookmarkButtonBase(listener
, title
),
248 bool GetTooltipText(const gfx::Point
& p
,
249 base::string16
* tooltip
) const override
{
250 gfx::Point
location(p
);
251 ConvertPointToScreen(this, &location
);
252 *tooltip
= BookmarkBarView::CreateToolTipForURLAndTitle(
253 GetWidget(), location
, url_
, GetText(), profile_
);
254 return !tooltip
->empty();
257 const char* GetClassName() const override
{ return kViewClassName
; }
263 DISALLOW_COPY_AND_ASSIGN(BookmarkButton
);
267 const char BookmarkButton::kViewClassName
[] = "BookmarkButton";
269 // ShortcutButton -------------------------------------------------------------
271 // Buttons used for the shortcuts on the bookmark bar.
273 class ShortcutButton
: public BookmarkButtonBase
{
275 // The internal view class name.
276 static const char kViewClassName
[];
278 ShortcutButton(views::ButtonListener
* listener
,
279 const base::string16
& title
)
280 : BookmarkButtonBase(listener
, title
) {
283 const char* GetClassName() const override
{ return kViewClassName
; }
286 DISALLOW_COPY_AND_ASSIGN(ShortcutButton
);
290 const char ShortcutButton::kViewClassName
[] = "ShortcutButton";
292 // BookmarkFolderButton -------------------------------------------------------
294 // Buttons used for folders on the bookmark bar, including the 'other folders'
296 class BookmarkFolderButton
: public views::MenuButton
{
298 BookmarkFolderButton(views::ButtonListener
* listener
,
299 const base::string16
& title
,
300 views::MenuButtonListener
* menu_button_listener
,
301 bool show_menu_marker
)
302 : MenuButton(listener
, title
, menu_button_listener
, show_menu_marker
) {
303 SetElideBehavior(kElideBehavior
);
304 show_animation_
.reset(new gfx::SlideAnimation(this));
305 if (!animations_enabled
) {
306 // For some reason during testing the events generated by animating
307 // throw off the test. So, don't animate while testing.
308 show_animation_
->Reset(1);
310 show_animation_
->Show();
314 bool GetTooltipText(const gfx::Point
& p
,
315 base::string16
* tooltip
) const override
{
316 if (label()->GetPreferredSize().width() > label()->size().width())
317 *tooltip
= GetText();
318 return !tooltip
->empty();
321 bool IsTriggerableEvent(const ui::Event
& e
) override
{
322 // Left clicks and taps should show the menu contents and right clicks
323 // should show the context menu. They should not trigger the opening of
325 if (e
.type() == ui::ET_GESTURE_TAP
||
326 (e
.IsMouseEvent() && (e
.flags() &
327 (ui::EF_LEFT_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
))))
330 if (e
.IsMouseEvent())
331 return ui::DispositionFromEventFlags(e
.flags()) != CURRENT_TAB
;
336 scoped_ptr
<gfx::SlideAnimation
> show_animation_
;
338 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton
);
341 // OverFlowButton (chevron) --------------------------------------------------
343 class OverFlowButton
: public views::MenuButton
{
345 explicit OverFlowButton(BookmarkBarView
* owner
)
346 : MenuButton(NULL
, base::string16(), owner
, false),
349 bool OnMousePressed(const ui::MouseEvent
& e
) override
{
350 owner_
->StopThrobbing(true);
351 return views::MenuButton::OnMousePressed(e
);
355 BookmarkBarView
* owner_
;
357 DISALLOW_COPY_AND_ASSIGN(OverFlowButton
);
360 void RecordAppLaunch(Profile
* profile
, const GURL
& url
) {
361 const extensions::Extension
* extension
=
362 extensions::ExtensionRegistry::Get(profile
)
363 ->enabled_extensions().GetAppByURL(url
);
367 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR
,
368 extension
->GetType());
373 // DropLocation ---------------------------------------------------------------
375 struct BookmarkBarView::DropLocation
{
378 operation(ui::DragDropTypes::DRAG_NONE
),
380 button_type(DROP_BOOKMARK
) {
383 bool Equals(const DropLocation
& other
) {
384 return ((other
.index
== index
) && (other
.on
== on
) &&
385 (other
.button_type
== button_type
));
388 // Index into the model the drop is over. This is relative to the root node.
394 // If true, the user is dropping on a folder.
398 DropButtonType button_type
;
401 // DropInfo -------------------------------------------------------------------
403 // Tracks drops on the BookmarkBarView.
405 struct BookmarkBarView::DropInfo
{
408 is_menu_showing(false),
413 // Whether the data is valid.
416 // If true, the menu is being shown.
417 bool is_menu_showing
;
419 // Coordinates of the drag (in terms of the BookmarkBarView).
423 // DropData for the drop.
424 BookmarkNodeData data
;
426 DropLocation location
;
429 // ButtonSeparatorView --------------------------------------------------------
431 // Paints a themed gradient divider at location |x|. |height| is the full
432 // height of the view you want to paint the divider into, not the height of
433 // the divider. The height of the divider will become:
434 // |height| - 2 * |vertical_padding|.
435 // The color of the divider is a gradient starting with |top_color| at the
436 // top, and changing into |middle_color| and then over to |bottom_color| as
437 // you go further down.
438 void PaintVerticalDivider(gfx::Canvas
* canvas
,
441 int vertical_padding
,
443 SkColor middle_color
,
444 SkColor bottom_color
) {
445 // Draw the upper half of the divider.
447 skia::RefPtr
<SkShader
> shader
= gfx::CreateGradientShader(
448 vertical_padding
+ 1, height
/ 2, top_color
, middle_color
);
449 paint
.setShader(shader
.get());
450 SkRect rc
= { SkIntToScalar(x
),
451 SkIntToScalar(vertical_padding
+ 1),
452 SkIntToScalar(x
+ 1),
453 SkIntToScalar(height
/ 2) };
454 canvas
->sk_canvas()->drawRect(rc
, paint
);
456 // Draw the lower half of the divider.
458 shader
= gfx::CreateGradientShader(
459 height
/ 2, height
- vertical_padding
, middle_color
, bottom_color
);
460 paint_down
.setShader(shader
.get());
461 SkRect rc_down
= { SkIntToScalar(x
),
462 SkIntToScalar(height
/ 2),
463 SkIntToScalar(x
+ 1),
464 SkIntToScalar(height
- vertical_padding
) };
465 canvas
->sk_canvas()->drawRect(rc_down
, paint_down
);
468 class BookmarkBarView::ButtonSeparatorView
: public views::View
{
470 ButtonSeparatorView() {}
471 ~ButtonSeparatorView() override
{}
473 void OnPaint(gfx::Canvas
* canvas
) override
{
474 PaintVerticalDivider(
481 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR
));
484 gfx::Size
GetPreferredSize() const override
{
485 // We get the full height of the bookmark bar, so that the height returned
486 // here doesn't matter.
487 return gfx::Size(kSeparatorWidth
, 1);
490 void GetAccessibleState(ui::AXViewState
* state
) override
{
491 state
->name
= l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR
);
492 state
->role
= ui::AX_ROLE_SPLITTER
;
496 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView
);
499 // BookmarkBarView ------------------------------------------------------------
502 const char BookmarkBarView::kViewClassName
[] = "BookmarkBarView";
504 BookmarkBarView::BookmarkBarView(Browser
* browser
, BrowserView
* browser_view
)
505 : page_navigator_(NULL
),
507 bookmark_menu_(NULL
),
508 bookmark_drop_menu_(NULL
),
509 other_bookmarks_button_(NULL
),
510 managed_bookmarks_button_(NULL
),
511 supervised_bookmarks_button_(NULL
),
512 apps_page_shortcut_(NULL
),
513 overflow_button_(NULL
),
515 bookmarks_separator_view_(NULL
),
517 browser_view_(browser_view
),
518 infobar_visible_(false),
519 throbbing_view_(NULL
),
520 bookmark_bar_state_(BookmarkBar::SHOW
),
521 animating_detached_(false),
522 show_folder_method_factory_(this) {
523 set_id(VIEW_ID_BOOKMARK_BAR
);
526 size_animation_
->Reset(1);
529 BookmarkBarView::~BookmarkBarView() {
531 model_
->RemoveObserver(this);
533 // It's possible for the menu to outlive us, reset the observer to make sure
534 // it doesn't have a reference to us.
535 if (bookmark_menu_
) {
536 bookmark_menu_
->set_observer(NULL
);
537 bookmark_menu_
->SetPageNavigator(NULL
);
538 bookmark_menu_
->clear_bookmark_bar();
540 if (context_menu_
.get())
541 context_menu_
->SetPageNavigator(NULL
);
543 StopShowFolderDropMenuTimer();
547 void BookmarkBarView::DisableAnimationsForTesting(bool disabled
) {
548 animations_enabled
= !disabled
;
551 void BookmarkBarView::AddObserver(BookmarkBarViewObserver
* observer
) {
552 observers_
.AddObserver(observer
);
555 void BookmarkBarView::RemoveObserver(BookmarkBarViewObserver
* observer
) {
556 observers_
.RemoveObserver(observer
);
559 void BookmarkBarView::SetPageNavigator(PageNavigator
* navigator
) {
560 page_navigator_
= navigator
;
562 bookmark_menu_
->SetPageNavigator(navigator
);
563 if (context_menu_
.get())
564 context_menu_
->SetPageNavigator(navigator
);
567 void BookmarkBarView::SetBookmarkBarState(
568 BookmarkBar::State state
,
569 BookmarkBar::AnimateChangeType animate_type
) {
570 if (animate_type
== BookmarkBar::ANIMATE_STATE_CHANGE
&&
571 animations_enabled
) {
572 animating_detached_
= (state
== BookmarkBar::DETACHED
||
573 bookmark_bar_state_
== BookmarkBar::DETACHED
);
574 if (state
== BookmarkBar::SHOW
)
575 size_animation_
->Show();
577 size_animation_
->Hide();
579 size_animation_
->Reset(state
== BookmarkBar::SHOW
? 1 : 0);
581 bookmark_bar_state_
= state
;
584 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const {
585 if (!infobar_visible_
&& browser_
->window()->IsFullscreen()) {
586 // There is no client edge to overlap when detached in fullscreen with no
590 return views::NonClientFrameView::kClientEdgeThickness
;
593 bool BookmarkBarView::is_animating() {
594 return size_animation_
->is_animating();
597 const BookmarkNode
* BookmarkBarView::GetNodeForButtonAtModelIndex(
598 const gfx::Point
& loc
,
599 int* model_start_index
) {
600 *model_start_index
= 0;
602 if (loc
.x() < 0 || loc
.x() >= width() || loc
.y() < 0 || loc
.y() >= height())
605 gfx::Point
adjusted_loc(GetMirroredXInView(loc
.x()), loc
.y());
607 // Check the managed button first.
608 if (managed_bookmarks_button_
->visible() &&
609 managed_bookmarks_button_
->bounds().Contains(adjusted_loc
)) {
610 return managed_
->managed_node();
613 // Then check the supervised button.
614 if (supervised_bookmarks_button_
->visible() &&
615 supervised_bookmarks_button_
->bounds().Contains(adjusted_loc
)) {
616 return managed_
->supervised_node();
619 // Then check the bookmark buttons.
620 for (int i
= 0; i
< GetBookmarkButtonCount(); ++i
) {
621 views::View
* child
= child_at(i
);
622 if (!child
->visible())
624 if (child
->bounds().Contains(adjusted_loc
))
625 return model_
->bookmark_bar_node()->GetChild(i
);
628 // Then the overflow button.
629 if (overflow_button_
->visible() &&
630 overflow_button_
->bounds().Contains(adjusted_loc
)) {
631 *model_start_index
= GetFirstHiddenNodeIndex();
632 return model_
->bookmark_bar_node();
635 // And finally the other folder.
636 if (other_bookmarks_button_
->visible() &&
637 other_bookmarks_button_
->bounds().Contains(adjusted_loc
)) {
638 return model_
->other_node();
644 views::MenuButton
* BookmarkBarView::GetMenuButtonForNode(
645 const BookmarkNode
* node
) {
646 if (node
== managed_
->managed_node())
647 return managed_bookmarks_button_
;
648 if (node
== managed_
->supervised_node())
649 return supervised_bookmarks_button_
;
650 if (node
== model_
->other_node())
651 return other_bookmarks_button_
;
652 if (node
== model_
->bookmark_bar_node())
653 return overflow_button_
;
654 int index
= model_
->bookmark_bar_node()->GetIndexOf(node
);
655 if (index
== -1 || !node
->is_folder())
657 return static_cast<views::MenuButton
*>(child_at(index
));
660 void BookmarkBarView::GetAnchorPositionForButton(
661 views::MenuButton
* button
,
662 views::MenuAnchorPosition
* anchor
) {
663 if (button
== other_bookmarks_button_
|| button
== overflow_button_
)
664 *anchor
= views::MENU_ANCHOR_TOPRIGHT
;
666 *anchor
= views::MENU_ANCHOR_TOPLEFT
;
669 views::MenuItemView
* BookmarkBarView::GetMenu() {
670 return bookmark_menu_
? bookmark_menu_
->menu() : NULL
;
673 views::MenuItemView
* BookmarkBarView::GetContextMenu() {
674 return bookmark_menu_
? bookmark_menu_
->context_menu() : NULL
;
677 views::MenuItemView
* BookmarkBarView::GetDropMenu() {
678 return bookmark_drop_menu_
? bookmark_drop_menu_
->menu() : NULL
;
681 void BookmarkBarView::StopThrobbing(bool immediate
) {
682 if (!throbbing_view_
)
685 // If not immediate, cycle through 2 more complete cycles.
686 throbbing_view_
->StartThrobbing(immediate
? 0 : 4);
687 throbbing_view_
= NULL
;
691 base::string16
BookmarkBarView::CreateToolTipForURLAndTitle(
692 const views::Widget
* widget
,
693 const gfx::Point
& screen_loc
,
695 const base::string16
& title
,
697 const views::TooltipManager
* tooltip_manager
= widget
->GetTooltipManager();
698 int max_width
= tooltip_manager
->GetMaxWidth(screen_loc
,
699 widget
->GetNativeView());
700 const gfx::FontList tt_fonts
= tooltip_manager
->GetFontList();
701 base::string16 result
;
704 if (!title
.empty()) {
705 base::string16 localized_title
= title
;
706 base::i18n::AdjustStringForLocaleDirection(&localized_title
);
707 result
.append(gfx::ElideText(localized_title
, tt_fonts
, max_width
,
711 // Only show the URL if the url and title differ.
712 if (title
!= base::UTF8ToUTF16(url
.spec())) {
714 result
.push_back('\n');
716 // We need to explicitly specify the directionality of the URL's text to
717 // make sure it is treated as an LTR string when the context is RTL. For
718 // example, the URL "http://www.yahoo.com/" appears as
719 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
720 // the Unicode BiDi algorithm puts certain characters on the left by
722 std::string languages
= profile
->GetPrefs()->GetString(
723 prefs::kAcceptLanguages
);
724 base::string16
elided_url(
725 url_formatter::ElideUrl(url
, tt_fonts
, max_width
, languages
));
726 elided_url
= base::i18n::GetDisplayStringInLTRDirectionality(elided_url
);
727 result
.append(elided_url
);
732 bool BookmarkBarView::IsDetached() const {
733 return (bookmark_bar_state_
== BookmarkBar::DETACHED
) ||
734 (animating_detached_
&& size_animation_
->is_animating());
737 double BookmarkBarView::GetAnimationValue() const {
738 return size_animation_
->GetCurrentValue();
741 int BookmarkBarView::GetToolbarOverlap() const {
742 int attached_overlap
= kToolbarAttachedBookmarkBarOverlap
+
743 views::NonClientFrameView::kClientEdgeThickness
;
745 return attached_overlap
;
747 int detached_overlap
= GetFullyDetachedToolbarOverlap();
749 // Do not animate the overlap when the infobar is above us (i.e. when we're
750 // detached), since drawing over the infobar looks weird.
751 if (infobar_visible_
)
752 return detached_overlap
;
754 // When detached with no infobar, animate the overlap between the attached and
756 return detached_overlap
+ static_cast<int>(
757 (attached_overlap
- detached_overlap
) *
758 size_animation_
->GetCurrentValue());
761 gfx::Size
BookmarkBarView::GetPreferredSize() const {
765 chrome::kBookmarkBarHeight
+
767 (chrome::kNTPBookmarkBarHeight
- chrome::kBookmarkBarHeight
) *
768 (1 - size_animation_
->GetCurrentValue())));
770 prefsize
.set_height(static_cast<int>(chrome::kBookmarkBarHeight
*
771 size_animation_
->GetCurrentValue()));
776 bool BookmarkBarView::CanProcessEventsWithinSubtree() const {
777 // If the bookmark bar is attached and the omnibox popup is open (on top of
778 // the bar), prevent events from targeting the bookmark bar or any of its
779 // descendants. This will prevent hovers/clicks just above the omnibox popup
780 // from activating the top few pixels of items on the bookmark bar.
781 if (!IsDetached() && browser_view_
&&
782 browser_view_
->GetLocationBar()->GetOmniboxView()->model()->
783 popup_model()->IsOpen()) {
789 gfx::Size
BookmarkBarView::GetMinimumSize() const {
790 // The minimum width of the bookmark bar should at least contain the overflow
791 // button, by which one can access all the Bookmark Bar items, and the "Other
792 // Bookmarks" folder, along with appropriate margins and button padding.
793 // It should also contain the Managed and/or Supervised Bookmarks folders,
794 // if they are visible.
795 int width
= kLeftMargin
;
797 int height
= chrome::kBookmarkBarHeight
;
799 double current_state
= 1 - size_animation_
->GetCurrentValue();
800 width
+= 2 * static_cast<int>(kNewTabHorizontalPadding
* current_state
);
801 height
+= static_cast<int>(
802 (chrome::kNTPBookmarkBarHeight
- chrome::kBookmarkBarHeight
) *
806 if (managed_bookmarks_button_
->visible()) {
807 gfx::Size size
= managed_bookmarks_button_
->GetPreferredSize();
808 width
+= size
.width() + kButtonPadding
;
810 if (supervised_bookmarks_button_
->visible()) {
811 gfx::Size size
= supervised_bookmarks_button_
->GetPreferredSize();
812 width
+= size
.width() + kButtonPadding
;
814 if (other_bookmarks_button_
->visible()) {
815 gfx::Size size
= other_bookmarks_button_
->GetPreferredSize();
816 width
+= size
.width() + kButtonPadding
;
818 if (overflow_button_
->visible()) {
819 gfx::Size size
= overflow_button_
->GetPreferredSize();
820 width
+= size
.width() + kButtonPadding
;
822 if (bookmarks_separator_view_
->visible()) {
823 gfx::Size size
= bookmarks_separator_view_
->GetPreferredSize();
824 width
+= size
.width();
826 if (apps_page_shortcut_
->visible()) {
827 gfx::Size size
= apps_page_shortcut_
->GetPreferredSize();
828 width
+= size
.width() + kButtonPadding
;
831 return gfx::Size(width
, height
);
834 void BookmarkBarView::Layout() {
835 // Skip layout during destruction, when no model exists.
840 int top_margin
= IsDetached() ? kDetachedTopMargin
: 0;
842 int width
= View::width() - kRightMargin
- kLeftMargin
;
843 int height
= chrome::kBookmarkBarHeight
- kBottomMargin
;
844 int separator_margin
= kSeparatorMargin
;
847 double current_state
= 1 - size_animation_
->GetCurrentValue();
848 x
+= static_cast<int>(kNewTabHorizontalPadding
* current_state
);
849 y
+= (View::height() - chrome::kBookmarkBarHeight
) / 2;
850 width
-= static_cast<int>(kNewTabHorizontalPadding
* current_state
);
851 separator_margin
-= static_cast<int>(kSeparatorMargin
* current_state
);
853 // For the attached appearance, pin the content to the bottom of the bar
854 // when animating in/out, as shrinking its height instead looks weird. This
855 // also matches how we layout infobars.
856 y
+= View::height() - chrome::kBookmarkBarHeight
;
859 gfx::Size other_bookmarks_pref
= other_bookmarks_button_
->visible() ?
860 other_bookmarks_button_
->GetPreferredSize() : gfx::Size();
861 gfx::Size overflow_pref
= overflow_button_
->GetPreferredSize();
862 gfx::Size bookmarks_separator_pref
=
863 bookmarks_separator_view_
->GetPreferredSize();
864 gfx::Size apps_page_shortcut_pref
= apps_page_shortcut_
->visible() ?
865 apps_page_shortcut_
->GetPreferredSize() : gfx::Size();
867 int max_x
= width
- overflow_pref
.width() - kButtonPadding
-
868 bookmarks_separator_pref
.width();
869 if (other_bookmarks_button_
->visible())
870 max_x
-= other_bookmarks_pref
.width() + kButtonPadding
;
872 // Start with the apps page shortcut button.
873 if (apps_page_shortcut_
->visible()) {
874 apps_page_shortcut_
->SetBounds(x
, y
, apps_page_shortcut_pref
.width(),
876 x
+= apps_page_shortcut_pref
.width() + kButtonPadding
;
879 // Then comes the managed bookmarks folder, if visible.
880 if (managed_bookmarks_button_
->visible()) {
881 gfx::Size managed_bookmarks_pref
=
882 managed_bookmarks_button_
->GetPreferredSize();
883 managed_bookmarks_button_
->SetBounds(x
, y
, managed_bookmarks_pref
.width(),
885 x
+= managed_bookmarks_pref
.width() + kButtonPadding
;
888 // Then the supervised bookmarks folder, if visible.
889 if (supervised_bookmarks_button_
->visible()) {
890 gfx::Size supervised_bookmarks_pref
=
891 supervised_bookmarks_button_
->GetPreferredSize();
892 supervised_bookmarks_button_
->SetBounds(
893 x
, y
, supervised_bookmarks_pref
.width(), height
);
894 x
+= supervised_bookmarks_pref
.width() + kButtonPadding
;
897 const bool show_instructions
=
898 model_
&& model_
->loaded() &&
899 model_
->bookmark_bar_node()->child_count() == 0;
900 instructions_
->SetVisible(show_instructions
);
901 if (show_instructions
) {
902 gfx::Size pref
= instructions_
->GetPreferredSize();
903 instructions_
->SetBounds(
904 x
+ kInstructionsPadding
, y
,
905 std::min(static_cast<int>(pref
.width()),
909 bool last_visible
= x
< max_x
;
910 int button_count
= GetBookmarkButtonCount();
911 for (int i
= 0; i
<= button_count
; ++i
) {
912 if (i
== button_count
) {
913 // Add another button if there is room for it (and there is another
915 if (!last_visible
|| !model_
->loaded() ||
916 model_
->bookmark_bar_node()->child_count() <= button_count
)
919 CreateBookmarkButton(model_
->bookmark_bar_node()->GetChild(i
)), i
);
920 button_count
= GetBookmarkButtonCount();
922 views::View
* child
= child_at(i
);
923 gfx::Size pref
= child
->GetPreferredSize();
924 int next_x
= x
+ pref
.width() + kButtonPadding
;
925 last_visible
= next_x
< max_x
;
926 child
->SetVisible(last_visible
);
927 // Only need to set bounds if the view is actually visible.
929 child
->SetBounds(x
, y
, pref
.width(), height
);
934 // Layout the right side buttons.
935 x
= max_x
+ kButtonPadding
;
937 // The overflow button.
938 overflow_button_
->SetBounds(x
, y
, overflow_pref
.width(), height
);
939 const bool show_overflow
=
941 (model_
->bookmark_bar_node()->child_count() > GetBookmarkButtonCount() ||
942 (GetBookmarkButtonCount() > 0 &&
943 !GetBookmarkButton(GetBookmarkButtonCount() - 1)->visible()));
944 overflow_button_
->SetVisible(show_overflow
);
945 x
+= overflow_pref
.width();
948 if (bookmarks_separator_view_
->visible()) {
949 bookmarks_separator_view_
->SetBounds(x
,
951 bookmarks_separator_pref
.width(),
952 height
+ top_margin
+ kBottomMargin
-
955 x
+= bookmarks_separator_pref
.width();
958 // The "Other Bookmarks" button.
959 if (other_bookmarks_button_
->visible()) {
960 other_bookmarks_button_
->SetBounds(x
, y
, other_bookmarks_pref
.width(),
962 x
+= other_bookmarks_pref
.width() + kButtonPadding
;
966 void BookmarkBarView::ViewHierarchyChanged(
967 const ViewHierarchyChangedDetails
& details
) {
968 if (details
.is_add
&& details
.child
== this) {
969 // We may get inserted into a hierarchy with a profile - this typically
970 // occurs when the bar's contents get populated fast enough that the
971 // buttons are created before the bar is attached to a frame.
975 // We only layout while parented. When we become parented, if our bounds
976 // haven't changed, OnBoundsChanged() won't get invoked and we won't
977 // layout. Therefore we always force a layout when added.
983 void BookmarkBarView::PaintChildren(const ui::PaintContext
& context
) {
984 View::PaintChildren(context
);
986 if (drop_info_
.get() && drop_info_
->valid
&&
987 drop_info_
->location
.operation
!= 0 && drop_info_
->location
.index
!= -1 &&
988 drop_info_
->location
.button_type
!= DROP_OVERFLOW
&&
989 !drop_info_
->location
.on
) {
990 int index
= drop_info_
->location
.index
;
991 DCHECK(index
<= GetBookmarkButtonCount());
995 if (index
== GetBookmarkButtonCount()) {
999 x
= GetBookmarkButton(index
- 1)->x() +
1000 GetBookmarkButton(index
- 1)->width();
1003 x
= GetBookmarkButton(index
)->x();
1005 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
1006 y
= GetBookmarkButton(0)->y();
1007 h
= GetBookmarkButton(0)->height();
1010 // Since the drop indicator is painted directly onto the canvas, we must
1011 // make sure it is painted in the right location if the locale is RTL.
1012 gfx::Rect
indicator_bounds(x
- kDropIndicatorWidth
/ 2,
1014 kDropIndicatorWidth
,
1016 indicator_bounds
.set_x(GetMirroredXForRect(indicator_bounds
));
1018 ui::PaintRecorder
recorder(context
, size());
1019 // TODO(sky/glen): make me pretty!
1020 recorder
.canvas()->FillRect(indicator_bounds
, kDropIndicatorColor
);
1024 bool BookmarkBarView::GetDropFormats(
1026 std::set
<ui::OSExchangeData::CustomFormat
>* custom_formats
) {
1027 if (!model_
|| !model_
->loaded())
1029 *formats
= ui::OSExchangeData::URL
;
1030 custom_formats
->insert(BookmarkNodeData::GetBookmarkFormatType());
1034 bool BookmarkBarView::AreDropTypesRequired() {
1038 bool BookmarkBarView::CanDrop(const ui::OSExchangeData
& data
) {
1039 if (!model_
|| !model_
->loaded() ||
1040 !browser_
->profile()->GetPrefs()->GetBoolean(
1041 bookmarks::prefs::kEditBookmarksEnabled
))
1044 if (!drop_info_
.get())
1045 drop_info_
.reset(new DropInfo());
1047 // Only accept drops of 1 node, which is the case for all data dragged from
1048 // bookmark bar and menus.
1049 return drop_info_
->data
.Read(data
) && drop_info_
->data
.size() == 1;
1052 void BookmarkBarView::OnDragEntered(const DropTargetEvent
& event
) {
1055 int BookmarkBarView::OnDragUpdated(const DropTargetEvent
& event
) {
1056 if (!drop_info_
.get())
1059 if (drop_info_
->valid
&&
1060 (drop_info_
->x
== event
.x() && drop_info_
->y
== event
.y())) {
1061 // The location of the mouse didn't change, return the last operation.
1062 return drop_info_
->location
.operation
;
1065 drop_info_
->x
= event
.x();
1066 drop_info_
->y
= event
.y();
1068 DropLocation location
;
1069 CalculateDropLocation(event
, drop_info_
->data
, &location
);
1071 if (drop_info_
->valid
&& drop_info_
->location
.Equals(location
)) {
1072 // The position we're going to drop didn't change, return the last drag
1073 // operation we calculated. Copy of the operation in case it changed.
1074 drop_info_
->location
.operation
= location
.operation
;
1075 return drop_info_
->location
.operation
;
1078 StopShowFolderDropMenuTimer();
1080 // TODO(sky): Optimize paint region.
1083 drop_info_
->location
= location
;
1084 drop_info_
->valid
= true;
1086 if (drop_info_
->is_menu_showing
) {
1087 if (bookmark_drop_menu_
)
1088 bookmark_drop_menu_
->Cancel();
1089 drop_info_
->is_menu_showing
= false;
1092 if (location
.on
|| location
.button_type
== DROP_OVERFLOW
||
1093 location
.button_type
== DROP_OTHER_FOLDER
) {
1094 const BookmarkNode
* node
;
1095 if (location
.button_type
== DROP_OTHER_FOLDER
)
1096 node
= model_
->other_node();
1097 else if (location
.button_type
== DROP_OVERFLOW
)
1098 node
= model_
->bookmark_bar_node();
1100 node
= model_
->bookmark_bar_node()->GetChild(location
.index
);
1101 StartShowFolderDropMenuTimer(node
);
1104 return drop_info_
->location
.operation
;
1107 void BookmarkBarView::OnDragExited() {
1108 StopShowFolderDropMenuTimer();
1110 // NOTE: we don't hide the menu on exit as it's possible the user moved the
1111 // mouse over the menu, which triggers an exit on us.
1113 drop_info_
->valid
= false;
1115 if (drop_info_
->location
.index
!= -1) {
1116 // TODO(sky): optimize the paint region.
1122 int BookmarkBarView::OnPerformDrop(const DropTargetEvent
& event
) {
1123 StopShowFolderDropMenuTimer();
1125 if (bookmark_drop_menu_
)
1126 bookmark_drop_menu_
->Cancel();
1128 if (!drop_info_
.get() || !drop_info_
->location
.operation
)
1129 return ui::DragDropTypes::DRAG_NONE
;
1131 const BookmarkNode
* root
=
1132 (drop_info_
->location
.button_type
== DROP_OTHER_FOLDER
) ?
1133 model_
->other_node() : model_
->bookmark_bar_node();
1134 int index
= drop_info_
->location
.index
;
1137 // TODO(sky): optimize the SchedulePaint region.
1140 const BookmarkNode
* parent_node
;
1141 if (drop_info_
->location
.button_type
== DROP_OTHER_FOLDER
) {
1143 index
= parent_node
->child_count();
1144 } else if (drop_info_
->location
.on
) {
1145 parent_node
= root
->GetChild(index
);
1146 index
= parent_node
->child_count();
1150 const BookmarkNodeData data
= drop_info_
->data
;
1151 DCHECK(data
.is_valid());
1152 bool copy
= drop_info_
->location
.operation
== ui::DragDropTypes::DRAG_COPY
;
1154 return chrome::DropBookmarks(
1155 browser_
->profile(), data
, parent_node
, index
, copy
);
1158 void BookmarkBarView::OnThemeChanged() {
1162 const char* BookmarkBarView::GetClassName() const {
1163 return kViewClassName
;
1166 void BookmarkBarView::SetVisible(bool v
) {
1170 View::SetVisible(v
);
1171 FOR_EACH_OBSERVER(BookmarkBarViewObserver
, observers_
,
1172 OnBookmarkBarVisibilityChanged());
1175 void BookmarkBarView::GetAccessibleState(ui::AXViewState
* state
) {
1176 state
->role
= ui::AX_ROLE_TOOLBAR
;
1177 state
->name
= l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS
);
1180 void BookmarkBarView::AnimationProgressed(const gfx::Animation
* animation
) {
1181 // |browser_view_| can be NULL during tests.
1183 browser_view_
->ToolbarSizeChanged(true);
1186 void BookmarkBarView::AnimationEnded(const gfx::Animation
* animation
) {
1187 // |browser_view_| can be NULL during tests.
1188 if (browser_view_
) {
1189 browser_view_
->ToolbarSizeChanged(false);
1194 void BookmarkBarView::BookmarkMenuControllerDeleted(
1195 BookmarkMenuController
* controller
) {
1196 if (controller
== bookmark_menu_
)
1197 bookmark_menu_
= NULL
;
1198 else if (controller
== bookmark_drop_menu_
)
1199 bookmark_drop_menu_
= NULL
;
1202 void BookmarkBarView::OnImportBookmarks() {
1203 int64 install_time
= g_browser_process
->metrics_service()->GetInstallDate();
1204 int64 time_from_install
= base::Time::Now().ToTimeT() - install_time
;
1205 if (bookmark_bar_state_
== BookmarkBar::SHOW
) {
1206 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
1208 } else if (bookmark_bar_state_
== BookmarkBar::DETACHED
) {
1209 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1213 chrome::ShowImportDialog(browser_
);
1216 void BookmarkBarView::OnBookmarkBubbleShown(const BookmarkNode
* node
) {
1217 StopThrobbing(true);
1219 return; // Generally shouldn't happen.
1220 StartThrobbing(node
, false);
1223 void BookmarkBarView::OnBookmarkBubbleHidden() {
1224 StopThrobbing(false);
1227 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel
* model
,
1228 bool ids_reassigned
) {
1229 // There should be no buttons. If non-zero it means Load was invoked more than
1230 // once, or we didn't properly clear things. Either of which shouldn't happen.
1231 // The actual bookmark buttons are added from Layout().
1232 DCHECK_EQ(0, GetBookmarkButtonCount());
1233 DCHECK(model
->other_node());
1234 other_bookmarks_button_
->SetAccessibleName(model
->other_node()->GetTitle());
1235 other_bookmarks_button_
->SetText(model
->other_node()->GetTitle());
1236 managed_bookmarks_button_
->SetAccessibleName(
1237 managed_
->managed_node()->GetTitle());
1238 managed_bookmarks_button_
->SetText(managed_
->managed_node()->GetTitle());
1239 supervised_bookmarks_button_
->SetAccessibleName(
1240 managed_
->supervised_node()->GetTitle());
1241 supervised_bookmarks_button_
->SetText(
1242 managed_
->supervised_node()->GetTitle());
1244 UpdateOtherAndManagedButtonsVisibility();
1245 other_bookmarks_button_
->SetEnabled(true);
1246 managed_bookmarks_button_
->SetEnabled(true);
1247 supervised_bookmarks_button_
->SetEnabled(true);
1251 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel
* model
) {
1253 // Do minimal cleanup, presumably we'll be deleted shortly.
1254 model_
->RemoveObserver(this);
1258 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel
* model
,
1259 const BookmarkNode
* old_parent
,
1261 const BookmarkNode
* new_parent
,
1263 bool was_throbbing
= throbbing_view_
&&
1264 throbbing_view_
== DetermineViewToThrobFromRemove(old_parent
, old_index
);
1266 throbbing_view_
->StopThrobbing();
1267 bool needs_layout_and_paint
=
1268 BookmarkNodeRemovedImpl(model
, old_parent
, old_index
);
1269 if (BookmarkNodeAddedImpl(model
, new_parent
, new_index
))
1270 needs_layout_and_paint
= true;
1271 if (was_throbbing
&& new_index
< GetBookmarkButtonCount())
1272 StartThrobbing(new_parent
->GetChild(new_index
), false);
1273 if (needs_layout_and_paint
)
1277 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel
* model
,
1278 const BookmarkNode
* parent
,
1280 if (BookmarkNodeAddedImpl(model
, parent
, index
))
1284 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel
* model
,
1285 const BookmarkNode
* parent
,
1287 const BookmarkNode
* node
,
1288 const std::set
<GURL
>& removed_urls
) {
1289 // Close the menu if the menu is showing for the deleted node.
1290 if (bookmark_menu_
&& bookmark_menu_
->node() == node
)
1291 bookmark_menu_
->Cancel();
1292 if (BookmarkNodeRemovedImpl(model
, parent
, old_index
))
1296 void BookmarkBarView::BookmarkAllUserNodesRemoved(
1297 BookmarkModel
* model
,
1298 const std::set
<GURL
>& removed_urls
) {
1299 UpdateOtherAndManagedButtonsVisibility();
1301 StopThrobbing(true);
1303 // Remove the existing buttons.
1304 while (GetBookmarkButtonCount())
1305 delete GetBookmarkButton(0);
1310 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel
* model
,
1311 const BookmarkNode
* node
) {
1312 BookmarkNodeChangedImpl(model
, node
);
1315 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel
* model
,
1316 const BookmarkNode
* node
) {
1317 if (node
!= model
->bookmark_bar_node())
1318 return; // We only care about reordering of the bookmark bar node.
1320 // Remove the existing buttons.
1321 while (GetBookmarkButtonCount())
1324 // Create the new buttons.
1325 for (int i
= 0, child_count
= node
->child_count(); i
< child_count
; ++i
)
1326 AddChildViewAt(CreateBookmarkButton(node
->GetChild(i
)), i
);
1331 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel
* model
,
1332 const BookmarkNode
* node
) {
1333 BookmarkNodeChangedImpl(model
, node
);
1336 void BookmarkBarView::WriteDragDataForView(View
* sender
,
1337 const gfx::Point
& press_pt
,
1338 ui::OSExchangeData
* data
) {
1339 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1341 for (int i
= 0; i
< GetBookmarkButtonCount(); ++i
) {
1342 if (sender
== GetBookmarkButton(i
)) {
1343 const BookmarkNode
* node
= model_
->bookmark_bar_node()->GetChild(i
);
1344 gfx::ImageSkia icon
;
1345 if (node
->is_url()) {
1346 const gfx::Image
& image
= model_
->GetFavicon(node
);
1347 icon
= image
.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON
)
1348 : image
.AsImageSkia();
1350 icon
= chrome::GetBookmarkFolderIcon();
1353 button_drag_utils::SetDragImage(node
->url(), node
->GetTitle(), icon
,
1355 GetBookmarkButton(i
)->GetWidget());
1356 WriteBookmarkDragData(node
, data
);
1363 int BookmarkBarView::GetDragOperationsForView(View
* sender
,
1364 const gfx::Point
& p
) {
1365 if (size_animation_
->is_animating() ||
1366 (size_animation_
->GetCurrentValue() == 0 &&
1367 bookmark_bar_state_
!= BookmarkBar::DETACHED
)) {
1368 // Don't let the user drag while animating open or we're closed (and not
1369 // detached, when detached size_animation_ is always 0). This typically is
1370 // only hit if the user does something to inadvertently trigger DnD such as
1371 // pressing the mouse and hitting control-b.
1372 return ui::DragDropTypes::DRAG_NONE
;
1375 for (int i
= 0; i
< GetBookmarkButtonCount(); ++i
) {
1376 if (sender
== GetBookmarkButton(i
)) {
1377 return chrome::GetBookmarkDragOperation(
1378 browser_
->profile(), model_
->bookmark_bar_node()->GetChild(i
));
1382 return ui::DragDropTypes::DRAG_NONE
;
1385 bool BookmarkBarView::CanStartDragForView(views::View
* sender
,
1386 const gfx::Point
& press_pt
,
1387 const gfx::Point
& p
) {
1388 // Check if we have not moved enough horizontally but we have moved downward
1389 // vertically - downward drag.
1390 gfx::Vector2d move_offset
= p
- press_pt
;
1391 gfx::Vector2d
horizontal_offset(move_offset
.x(), 0);
1392 if (!View::ExceededDragThreshold(horizontal_offset
) && move_offset
.y() > 0) {
1393 for (int i
= 0; i
< GetBookmarkButtonCount(); ++i
) {
1394 if (sender
== GetBookmarkButton(i
)) {
1395 const BookmarkNode
* node
= model_
->bookmark_bar_node()->GetChild(i
);
1396 // If the folder button was dragged, show the menu instead.
1397 if (node
&& node
->is_folder()) {
1398 views::MenuButton
* menu_button
=
1399 static_cast<views::MenuButton
*>(sender
);
1400 menu_button
->Activate();
1410 void BookmarkBarView::OnMenuButtonClicked(views::View
* view
,
1411 const gfx::Point
& point
) {
1412 const BookmarkNode
* node
;
1414 int start_index
= 0;
1415 if (view
== other_bookmarks_button_
) {
1416 node
= model_
->other_node();
1417 } else if (view
== managed_bookmarks_button_
) {
1418 node
= managed_
->managed_node();
1419 } else if (view
== supervised_bookmarks_button_
) {
1420 node
= managed_
->supervised_node();
1421 } else if (view
== overflow_button_
) {
1422 node
= model_
->bookmark_bar_node();
1423 start_index
= GetFirstHiddenNodeIndex();
1425 int button_index
= GetIndexOf(view
);
1426 DCHECK_NE(-1, button_index
);
1427 node
= model_
->bookmark_bar_node()->GetChild(button_index
);
1430 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1431 bookmark_menu_
= new BookmarkMenuController(
1432 browser_
, page_navigator_
, GetWidget(), node
, start_index
, false);
1433 bookmark_menu_
->set_observer(this);
1434 bookmark_menu_
->RunMenuAt(this);
1437 void BookmarkBarView::ButtonPressed(views::Button
* sender
,
1438 const ui::Event
& event
) {
1439 WindowOpenDisposition disposition_from_event_flags
=
1440 ui::DispositionFromEventFlags(event
.flags());
1442 if (sender
->tag() == kAppsShortcutButtonTag
) {
1443 OpenURLParams
params(GURL(chrome::kChromeUIAppsURL
),
1445 disposition_from_event_flags
,
1446 ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
1448 page_navigator_
->OpenURL(params
);
1449 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1453 const BookmarkNode
* node
;
1454 if (sender
->tag() == kOtherFolderButtonTag
) {
1455 node
= model_
->other_node();
1456 } else if (sender
->tag() == kManagedFolderButtonTag
) {
1457 node
= managed_
->managed_node();
1458 } else if (sender
->tag() == kSupervisedFolderButtonTag
) {
1459 node
= managed_
->supervised_node();
1461 int index
= GetIndexOf(sender
);
1462 DCHECK_NE(-1, index
);
1463 node
= model_
->bookmark_bar_node()->GetChild(index
);
1465 DCHECK(page_navigator_
);
1467 if (node
->is_url()) {
1468 RecordAppLaunch(browser_
->profile(), node
->url());
1469 OpenURLParams
params(
1470 node
->url(), Referrer(), disposition_from_event_flags
,
1471 ui::PAGE_TRANSITION_AUTO_BOOKMARK
, false);
1472 page_navigator_
->OpenURL(params
);
1474 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_
, node
,
1475 disposition_from_event_flags
, browser_
->profile());
1478 RecordBookmarkLaunch(node
, GetBookmarkLaunchLocation());
1481 void BookmarkBarView::ShowContextMenuForView(views::View
* source
,
1482 const gfx::Point
& point
,
1483 ui::MenuSourceType source_type
) {
1484 if (!model_
->loaded()) {
1485 // Don't do anything if the model isn't loaded.
1489 const BookmarkNode
* parent
= NULL
;
1490 std::vector
<const BookmarkNode
*> nodes
;
1491 if (source
== other_bookmarks_button_
) {
1492 parent
= model_
->other_node();
1493 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1494 // sure the user can't edit/delete the node in this case.
1495 nodes
.push_back(parent
);
1496 } else if (source
== managed_bookmarks_button_
) {
1497 parent
= managed_
->managed_node();
1498 nodes
.push_back(parent
);
1499 } else if (source
== supervised_bookmarks_button_
) {
1500 parent
= managed_
->supervised_node();
1501 nodes
.push_back(parent
);
1502 } else if (source
!= this && source
!= apps_page_shortcut_
) {
1503 // User clicked on one of the bookmark buttons, find which one they
1504 // clicked on, except for the apps page shortcut, which must behave as if
1505 // the user clicked on the bookmark bar background.
1506 int bookmark_button_index
= GetIndexOf(source
);
1507 DCHECK(bookmark_button_index
!= -1 &&
1508 bookmark_button_index
< GetBookmarkButtonCount());
1509 const BookmarkNode
* node
=
1510 model_
->bookmark_bar_node()->GetChild(bookmark_button_index
);
1511 nodes
.push_back(node
);
1512 parent
= node
->parent();
1514 parent
= model_
->bookmark_bar_node();
1515 nodes
.push_back(parent
);
1517 bool close_on_remove
=
1518 (parent
== model_
->other_node()) && (parent
->child_count() == 1);
1520 context_menu_
.reset(new BookmarkContextMenu(
1521 GetWidget(), browser_
, browser_
->profile(),
1522 browser_
->tab_strip_model()->GetActiveWebContents(),
1523 parent
, nodes
, close_on_remove
));
1524 context_menu_
->RunMenuAt(point
, source_type
);
1527 void BookmarkBarView::Init() {
1528 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1529 // return NULL. When we're inserted into a hierarchy, we'll call
1530 // UpdateColors(), which will set the appropriate colors for all the objects
1531 // added in this function.
1533 // Child views are traversed in the order they are added. Make sure the order
1534 // they are added matches the visual order.
1535 overflow_button_
= CreateOverflowButton();
1536 AddChildView(overflow_button_
);
1538 other_bookmarks_button_
= CreateOtherBookmarksButton();
1539 // We'll re-enable when the model is loaded.
1540 other_bookmarks_button_
->SetEnabled(false);
1541 AddChildView(other_bookmarks_button_
);
1543 managed_bookmarks_button_
= CreateManagedBookmarksButton();
1544 // Also re-enabled when the model is loaded.
1545 managed_bookmarks_button_
->SetEnabled(false);
1546 AddChildView(managed_bookmarks_button_
);
1548 supervised_bookmarks_button_
= CreateSupervisedBookmarksButton();
1549 // Also re-enabled when the model is loaded.
1550 supervised_bookmarks_button_
->SetEnabled(false);
1551 AddChildView(supervised_bookmarks_button_
);
1553 apps_page_shortcut_
= CreateAppsPageShortcutButton();
1554 AddChildView(apps_page_shortcut_
);
1555 profile_pref_registrar_
.Init(browser_
->profile()->GetPrefs());
1556 profile_pref_registrar_
.Add(
1557 bookmarks::prefs::kShowAppsShortcutInBookmarkBar
,
1558 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged
,
1559 base::Unretained(this)));
1560 profile_pref_registrar_
.Add(
1561 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar
,
1562 base::Bind(&BookmarkBarView::OnShowManagedBookmarksPrefChanged
,
1563 base::Unretained(this)));
1564 apps_page_shortcut_
->SetVisible(
1565 chrome::ShouldShowAppsShortcutInBookmarkBar(
1566 browser_
->profile(), browser_
->host_desktop_type()));
1568 bookmarks_separator_view_
= new ButtonSeparatorView();
1569 AddChildView(bookmarks_separator_view_
);
1570 UpdateBookmarksSeparatorVisibility();
1572 instructions_
= new BookmarkBarInstructionsView(this);
1573 AddChildView(instructions_
);
1575 set_context_menu_controller(this);
1577 size_animation_
.reset(new gfx::SlideAnimation(this));
1579 model_
= BookmarkModelFactory::GetForProfile(browser_
->profile());
1580 managed_
= ManagedBookmarkServiceFactory::GetForProfile(browser_
->profile());
1582 model_
->AddObserver(this);
1583 if (model_
->loaded())
1584 BookmarkModelLoaded(model_
, false);
1585 // else case: we'll receive notification back from the BookmarkModel when
1586 // done loading, then we'll populate the bar.
1590 int BookmarkBarView::GetBookmarkButtonCount() const {
1591 // We contain seven non-bookmark button views: managed bookmarks, supervised
1592 // bookmarks, other bookmarks, bookmarks separator, chevrons (for overflow),
1593 // apps page, and the instruction label.
1594 return child_count() - 7;
1597 views::LabelButton
* BookmarkBarView::GetBookmarkButton(int index
) {
1598 // CHECK as otherwise we may do the wrong cast.
1599 CHECK(index
>= 0 && index
< GetBookmarkButtonCount());
1600 return static_cast<views::LabelButton
*>(child_at(index
));
1603 BookmarkLaunchLocation
BookmarkBarView::GetBookmarkLaunchLocation() const {
1604 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR
:
1605 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR
;
1608 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1609 const int bb_count
= GetBookmarkButtonCount();
1610 for (int i
= 0; i
< bb_count
; ++i
) {
1611 if (!GetBookmarkButton(i
)->visible())
1617 MenuButton
* BookmarkBarView::CreateOtherBookmarksButton() {
1618 // Title is set in Loaded.
1619 MenuButton
* button
=
1620 new BookmarkFolderButton(this, base::string16(), this, false);
1621 button
->set_id(VIEW_ID_OTHER_BOOKMARKS
);
1622 button
->SetImage(views::Button::STATE_NORMAL
,
1623 chrome::GetBookmarkFolderIcon());
1624 button
->set_context_menu_controller(this);
1625 button
->set_tag(kOtherFolderButtonTag
);
1629 MenuButton
* BookmarkBarView::CreateManagedBookmarksButton() {
1630 // Title is set in Loaded.
1631 MenuButton
* button
=
1632 new BookmarkFolderButton(this, base::string16(), this, false);
1633 button
->set_id(VIEW_ID_MANAGED_BOOKMARKS
);
1634 button
->SetImage(views::Button::STATE_NORMAL
,
1635 chrome::GetBookmarkManagedFolderIcon());
1636 button
->set_context_menu_controller(this);
1637 button
->set_tag(kManagedFolderButtonTag
);
1641 MenuButton
* BookmarkBarView::CreateSupervisedBookmarksButton() {
1642 // Title is set in Loaded.
1643 MenuButton
* button
=
1644 new BookmarkFolderButton(this, base::string16(), this, false);
1645 button
->set_id(VIEW_ID_SUPERVISED_BOOKMARKS
);
1646 button
->SetImage(views::Button::STATE_NORMAL
,
1647 chrome::GetBookmarkSupervisedFolderIcon());
1648 button
->set_context_menu_controller(this);
1649 button
->set_tag(kSupervisedFolderButtonTag
);
1653 MenuButton
* BookmarkBarView::CreateOverflowButton() {
1654 MenuButton
* button
= new OverFlowButton(this);
1655 button
->SetImage(views::Button::STATE_NORMAL
,
1656 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS
));
1658 // The overflow button's image contains an arrow and therefore it is a
1659 // direction sensitive image and we need to flip it if the UI layout is
1662 // By default, menu buttons are not flipped because they generally contain
1663 // text and flipping the gfx::Canvas object will break text rendering. Since
1664 // the overflow button does not contain text, we can safely flip it.
1665 button
->EnableCanvasFlippingForRTLUI(true);
1667 // Make visible as necessary.
1668 button
->SetVisible(false);
1669 // Set accessibility name.
1670 button
->SetAccessibleName(
1671 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON
));
1675 views::View
* BookmarkBarView::CreateBookmarkButton(const BookmarkNode
* node
) {
1676 if (node
->is_url()) {
1677 BookmarkButton
* button
= new BookmarkButton(
1678 this, node
->url(), node
->GetTitle(), browser_
->profile());
1679 ConfigureButton(node
, button
);
1682 views::MenuButton
* button
=
1683 new BookmarkFolderButton(this, node
->GetTitle(), this, false);
1684 button
->SetImage(views::Button::STATE_NORMAL
,
1685 chrome::GetBookmarkFolderIcon());
1686 ConfigureButton(node
, button
);
1690 views::LabelButton
* BookmarkBarView::CreateAppsPageShortcutButton() {
1691 views::LabelButton
* button
= new ShortcutButton(
1692 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME
));
1693 button
->SetTooltipText(l10n_util::GetStringUTF16(
1694 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP
));
1695 button
->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT
);
1696 button
->SetImage(views::Button::STATE_NORMAL
,
1697 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT
));
1698 button
->set_context_menu_controller(this);
1699 button
->set_tag(kAppsShortcutButtonTag
);
1703 void BookmarkBarView::ConfigureButton(const BookmarkNode
* node
,
1704 views::LabelButton
* button
) {
1705 button
->SetText(node
->GetTitle());
1706 button
->SetAccessibleName(node
->GetTitle());
1707 button
->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT
);
1708 // We don't always have a theme provider (ui tests, for example).
1709 if (GetThemeProvider()) {
1710 button
->SetTextColor(
1711 views::Button::STATE_NORMAL
,
1712 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
));
1715 button
->SetMinSize(gfx::Size());
1716 button
->set_context_menu_controller(this);
1717 button
->set_drag_controller(this);
1718 if (node
->is_url()) {
1719 const gfx::Image
& favicon
= model_
->GetFavicon(node
);
1720 button
->SetImage(views::Button::STATE_NORMAL
,
1721 favicon
.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON
)
1722 : *favicon
.ToImageSkia());
1724 button
->SetMaxSize(gfx::Size(kMaxButtonWidth
, 0));
1727 bool BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel
* model
,
1728 const BookmarkNode
* parent
,
1730 const bool needs_layout_and_paint
= UpdateOtherAndManagedButtonsVisibility();
1731 if (parent
!= model
->bookmark_bar_node())
1732 return needs_layout_and_paint
;
1733 if (index
< GetBookmarkButtonCount()) {
1734 const BookmarkNode
* node
= parent
->GetChild(index
);
1735 AddChildViewAt(CreateBookmarkButton(node
), index
);
1738 // If the new node was added after the last button we've created we may be
1739 // able to fit it. Assume we can by returning true, which forces a Layout()
1740 // and creation of the button (if it fits).
1741 return index
== GetBookmarkButtonCount();
1744 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel
* model
,
1745 const BookmarkNode
* parent
,
1747 const bool needs_layout
= UpdateOtherAndManagedButtonsVisibility();
1749 StopThrobbing(true);
1750 // No need to start throbbing again as the bookmark bubble can't be up at
1751 // the same time as the user reorders.
1753 if (parent
!= model
->bookmark_bar_node()) {
1754 // Only children of the bookmark_bar_node get buttons.
1755 return needs_layout
;
1757 if (index
>= GetBookmarkButtonCount())
1758 return needs_layout
;
1760 delete child_at(index
);
1764 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel
* model
,
1765 const BookmarkNode
* node
) {
1766 if (node
== managed_
->managed_node()) {
1767 // The managed node may have its title updated.
1768 managed_bookmarks_button_
->SetAccessibleName(
1769 managed_
->managed_node()->GetTitle());
1770 managed_bookmarks_button_
->SetText(managed_
->managed_node()->GetTitle());
1773 if (node
== managed_
->supervised_node()) {
1774 // The supervised node may have its title updated.
1775 supervised_bookmarks_button_
->SetAccessibleName(
1776 managed_
->supervised_node()->GetTitle());
1777 supervised_bookmarks_button_
->SetText(
1778 managed_
->supervised_node()->GetTitle());
1782 if (node
->parent() != model
->bookmark_bar_node()) {
1783 // We only care about nodes on the bookmark bar.
1786 int index
= model
->bookmark_bar_node()->GetIndexOf(node
);
1787 DCHECK_NE(-1, index
);
1788 if (index
>= GetBookmarkButtonCount())
1789 return; // Buttons are created as needed.
1790 views::LabelButton
* button
= GetBookmarkButton(index
);
1791 const int old_pref_width
= button
->GetPreferredSize().width();
1792 ConfigureButton(node
, button
);
1793 if (old_pref_width
!= button
->GetPreferredSize().width())
1797 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode
* node
) {
1798 if (bookmark_drop_menu_
) {
1799 if (bookmark_drop_menu_
->node() == node
) {
1800 // Already showing for the specified node.
1803 bookmark_drop_menu_
->Cancel();
1806 views::MenuButton
* menu_button
= GetMenuButtonForNode(node
);
1810 int start_index
= 0;
1811 if (node
== model_
->bookmark_bar_node())
1812 start_index
= GetFirstHiddenNodeIndex();
1814 drop_info_
->is_menu_showing
= true;
1815 bookmark_drop_menu_
= new BookmarkMenuController(
1816 browser_
, page_navigator_
, GetWidget(), node
, start_index
, true);
1817 bookmark_drop_menu_
->set_observer(this);
1818 bookmark_drop_menu_
->RunMenuAt(this);
1821 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1822 show_folder_method_factory_
.InvalidateWeakPtrs();
1825 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode
* node
) {
1826 if (!animations_enabled
) {
1827 // So that tests can run as fast as possible disable the delay during
1829 ShowDropFolderForNode(node
);
1832 show_folder_method_factory_
.InvalidateWeakPtrs();
1833 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1834 FROM_HERE
, base::Bind(&BookmarkBarView::ShowDropFolderForNode
,
1835 show_folder_method_factory_
.GetWeakPtr(), node
),
1836 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1839 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent
& event
,
1840 const BookmarkNodeData
& data
,
1841 DropLocation
* location
) {
1843 DCHECK(model_
->loaded());
1844 DCHECK(data
.is_valid());
1846 *location
= DropLocation();
1848 // The drop event uses the screen coordinates while the child Views are
1849 // always laid out from left to right (even though they are rendered from
1850 // right-to-left on RTL locales). Thus, in order to make sure the drop
1851 // coordinates calculation works, we mirror the event's X coordinate if the
1853 int mirrored_x
= GetMirroredXInView(event
.x());
1856 const int other_delta_x
= mirrored_x
- other_bookmarks_button_
->x();
1857 Profile
* profile
= browser_
->profile();
1858 if (other_bookmarks_button_
->visible() && other_delta_x
>= 0 &&
1859 other_delta_x
< other_bookmarks_button_
->width()) {
1860 // Mouse is over 'other' folder.
1861 location
->button_type
= DROP_OTHER_FOLDER
;
1862 location
->on
= true;
1864 } else if (!GetBookmarkButtonCount()) {
1865 // No bookmarks, accept the drop.
1866 location
->index
= 0;
1867 const BookmarkNode
* node
= data
.GetFirstNode(model_
, profile
->GetPath());
1868 int ops
= node
&& managed_
->CanBeEditedByUser(node
) ?
1869 ui::DragDropTypes::DRAG_MOVE
:
1870 ui::DragDropTypes::DRAG_COPY
| ui::DragDropTypes::DRAG_LINK
;
1871 location
->operation
= chrome::GetPreferredBookmarkDropOperation(
1872 event
.source_operations(), ops
);
1876 for (int i
= 0; i
< GetBookmarkButtonCount() &&
1877 GetBookmarkButton(i
)->visible() && !found
; i
++) {
1878 views::LabelButton
* button
= GetBookmarkButton(i
);
1879 int button_x
= mirrored_x
- button
->x();
1880 int button_w
= button
->width();
1881 if (button_x
< button_w
) {
1883 const BookmarkNode
* node
= model_
->bookmark_bar_node()->GetChild(i
);
1884 if (node
->is_folder()) {
1885 if (button_x
<= views::kDropBetweenPixels
) {
1886 location
->index
= i
;
1887 } else if (button_x
< button_w
- views::kDropBetweenPixels
) {
1888 location
->index
= i
;
1889 location
->on
= true;
1891 location
->index
= i
+ 1;
1893 } else if (button_x
< button_w
/ 2) {
1894 location
->index
= i
;
1896 location
->index
= i
+ 1;
1903 if (overflow_button_
->visible()) {
1904 // Are we over the overflow button?
1905 int overflow_delta_x
= mirrored_x
- overflow_button_
->x();
1906 if (overflow_delta_x
>= 0 &&
1907 overflow_delta_x
< overflow_button_
->width()) {
1908 // Mouse is over overflow button.
1909 location
->index
= GetFirstHiddenNodeIndex();
1910 location
->button_type
= DROP_OVERFLOW
;
1911 } else if (overflow_delta_x
< 0) {
1912 // Mouse is after the last visible button but before overflow button;
1913 // use the last visible index.
1914 location
->index
= GetFirstHiddenNodeIndex();
1918 } else if (!other_bookmarks_button_
->visible() ||
1919 mirrored_x
< other_bookmarks_button_
->x()) {
1920 // Mouse is after the last visible button but before more recently
1921 // bookmarked; use the last visible index.
1922 location
->index
= GetFirstHiddenNodeIndex();
1929 const BookmarkNode
* parent
= (location
->button_type
== DROP_OTHER_FOLDER
) ?
1930 model_
->other_node() :
1931 model_
->bookmark_bar_node()->GetChild(location
->index
);
1932 location
->operation
= chrome::GetBookmarkDropOperation(
1933 profile
, event
, data
, parent
, parent
->child_count());
1934 if (!location
->operation
&& !data
.has_single_url() &&
1935 data
.GetFirstNode(model_
, profile
->GetPath()) == parent
) {
1936 // Don't open a menu if the node being dragged is the menu to open.
1937 location
->on
= false;
1940 location
->operation
= chrome::GetBookmarkDropOperation(
1941 profile
, event
, data
, model_
->bookmark_bar_node(), location
->index
);
1945 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode
* node
,
1946 ui::OSExchangeData
* data
) {
1947 DCHECK(node
&& data
);
1948 BookmarkNodeData
drag_data(node
);
1949 drag_data
.Write(browser_
->profile()->GetPath(), data
);
1952 void BookmarkBarView::StartThrobbing(const BookmarkNode
* node
,
1953 bool overflow_only
) {
1954 DCHECK(!throbbing_view_
);
1956 // Determine which visible button is showing the bookmark (or is an ancestor
1957 // of the bookmark).
1958 const BookmarkNode
* bbn
= model_
->bookmark_bar_node();
1959 const BookmarkNode
* parent_on_bb
= node
;
1960 while (parent_on_bb
) {
1961 const BookmarkNode
* parent
= parent_on_bb
->parent();
1964 parent_on_bb
= parent
;
1967 int index
= bbn
->GetIndexOf(parent_on_bb
);
1968 if (index
>= GetFirstHiddenNodeIndex()) {
1969 // Node is hidden, animate the overflow button.
1970 throbbing_view_
= overflow_button_
;
1971 } else if (!overflow_only
) {
1972 throbbing_view_
= static_cast<CustomButton
*>(child_at(index
));
1974 } else if (bookmarks::IsDescendantOf(node
, managed_
->managed_node())) {
1975 throbbing_view_
= managed_bookmarks_button_
;
1976 } else if (bookmarks::IsDescendantOf(node
, managed_
->supervised_node())) {
1977 throbbing_view_
= supervised_bookmarks_button_
;
1978 } else if (!overflow_only
) {
1979 throbbing_view_
= other_bookmarks_button_
;
1982 // Use a large number so that the button continues to throb.
1983 if (throbbing_view_
)
1984 throbbing_view_
->StartThrobbing(std::numeric_limits
<int>::max());
1987 views::CustomButton
* BookmarkBarView::DetermineViewToThrobFromRemove(
1988 const BookmarkNode
* parent
,
1990 const BookmarkNode
* bbn
= model_
->bookmark_bar_node();
1991 const BookmarkNode
* old_node
= parent
;
1992 int old_index_on_bb
= old_index
;
1993 while (old_node
&& old_node
!= bbn
) {
1994 const BookmarkNode
* parent
= old_node
->parent();
1995 if (parent
== bbn
) {
1996 old_index_on_bb
= bbn
->GetIndexOf(old_node
);
2002 if (old_index_on_bb
>= GetFirstHiddenNodeIndex()) {
2003 // Node is hidden, animate the overflow button.
2004 return overflow_button_
;
2006 return static_cast<CustomButton
*>(child_at(old_index_on_bb
));
2008 if (bookmarks::IsDescendantOf(parent
, managed_
->managed_node()))
2009 return managed_bookmarks_button_
;
2010 if (bookmarks::IsDescendantOf(parent
, managed_
->supervised_node()))
2011 return supervised_bookmarks_button_
;
2012 // Node wasn't on the bookmark bar, use the "Other Bookmarks" button.
2013 return other_bookmarks_button_
;
2016 void BookmarkBarView::UpdateColors() {
2017 // We don't always have a theme provider (ui tests, for example).
2018 const ui::ThemeProvider
* theme_provider
= GetThemeProvider();
2019 if (!theme_provider
)
2022 theme_provider
->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT
);
2023 for (int i
= 0; i
< GetBookmarkButtonCount(); ++i
)
2024 GetBookmarkButton(i
)->SetTextColor(views::Button::STATE_NORMAL
, color
);
2025 other_bookmarks_button_
->SetTextColor(views::Button::STATE_NORMAL
, color
);
2026 managed_bookmarks_button_
->SetTextColor(views::Button::STATE_NORMAL
, color
);
2027 supervised_bookmarks_button_
->SetTextColor(views::Button::STATE_NORMAL
,
2029 if (apps_page_shortcut_
->visible())
2030 apps_page_shortcut_
->SetTextColor(views::Button::STATE_NORMAL
, color
);
2033 bool BookmarkBarView::UpdateOtherAndManagedButtonsVisibility() {
2034 bool has_other_children
= !model_
->other_node()->empty();
2035 bool update_other
= has_other_children
!= other_bookmarks_button_
->visible();
2037 other_bookmarks_button_
->SetVisible(has_other_children
);
2038 UpdateBookmarksSeparatorVisibility();
2041 bool show_managed
= !managed_
->managed_node()->empty() &&
2042 browser_
->profile()->GetPrefs()->GetBoolean(
2043 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar
);
2044 bool update_managed
= show_managed
!= managed_bookmarks_button_
->visible();
2046 managed_bookmarks_button_
->SetVisible(show_managed
);
2048 bool show_supervised
= !managed_
->supervised_node()->empty();
2049 bool update_supervised
=
2050 show_supervised
!= supervised_bookmarks_button_
->visible();
2051 if (update_supervised
)
2052 supervised_bookmarks_button_
->SetVisible(show_supervised
);
2054 return update_other
|| update_managed
|| update_supervised
;
2057 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() {
2058 // Ash does not paint the bookmarks separator line because it looks odd on
2059 // the flat background. We keep it present for layout, but don't draw it.
2060 bookmarks_separator_view_
->SetVisible(
2061 browser_
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
&&
2062 other_bookmarks_button_
->visible());
2065 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() {
2066 DCHECK(apps_page_shortcut_
);
2067 // Only perform layout if required.
2068 bool visible
= chrome::ShouldShowAppsShortcutInBookmarkBar(
2069 browser_
->profile(), browser_
->host_desktop_type());
2070 if (apps_page_shortcut_
->visible() == visible
)
2072 apps_page_shortcut_
->SetVisible(visible
);
2073 UpdateBookmarksSeparatorVisibility();
2077 void BookmarkBarView::OnShowManagedBookmarksPrefChanged() {
2078 if (UpdateOtherAndManagedButtonsVisibility())