Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bar_view.cc
blob2cc9c3dd52370d65793c0d7463dab15dbd8dff5a
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/i18n/rtl.h"
14 #include "base/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;
107 using views::View;
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;
168 #if !defined(OS_WIN)
169 static const gfx::ElideBehavior kElideBehavior = gfx::FADE_TAIL;
170 #else
171 // Windows fade eliding causes text to darken; see http://crbug.com/388084
172 static const gfx::ElideBehavior kElideBehavior = gfx::ELIDE_TAIL;
173 #endif
175 namespace {
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 {
190 public:
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);
200 } else {
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);
224 private:
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 {
235 public:
236 // The internal view class name.
237 static const char kViewClassName[];
239 BookmarkButton(views::ButtonListener* listener,
240 const GURL& url,
241 const base::string16& title,
242 Profile* profile)
243 : BookmarkButtonBase(listener, title),
244 url_(url),
245 profile_(profile) {
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; }
259 private:
260 const GURL& url_;
261 Profile* profile_;
263 DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
266 // static
267 const char BookmarkButton::kViewClassName[] = "BookmarkButton";
269 // ShortcutButton -------------------------------------------------------------
271 // Buttons used for the shortcuts on the bookmark bar.
273 class ShortcutButton : public BookmarkButtonBase {
274 public:
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; }
285 private:
286 DISALLOW_COPY_AND_ASSIGN(ShortcutButton);
289 // static
290 const char ShortcutButton::kViewClassName[] = "ShortcutButton";
292 // BookmarkFolderButton -------------------------------------------------------
294 // Buttons used for folders on the bookmark bar, including the 'other folders'
295 // button.
296 class BookmarkFolderButton : public views::MenuButton {
297 public:
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);
309 } else {
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
324 // underlying urls.
325 if (e.type() == ui::ET_GESTURE_TAP ||
326 (e.IsMouseEvent() && (e.flags() &
327 (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON))))
328 return false;
330 if (e.IsMouseEvent())
331 return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB;
332 return false;
335 private:
336 scoped_ptr<gfx::SlideAnimation> show_animation_;
338 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
341 // OverFlowButton (chevron) --------------------------------------------------
343 class OverFlowButton : public views::MenuButton {
344 public:
345 explicit OverFlowButton(BookmarkBarView* owner)
346 : MenuButton(NULL, base::string16(), owner, false),
347 owner_(owner) {}
349 bool OnMousePressed(const ui::MouseEvent& e) override {
350 owner_->StopThrobbing(true);
351 return views::MenuButton::OnMousePressed(e);
354 private:
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);
364 if (!extension)
365 return;
367 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR,
368 extension->GetType());
371 } // namespace
373 // DropLocation ---------------------------------------------------------------
375 struct BookmarkBarView::DropLocation {
376 DropLocation()
377 : index(-1),
378 operation(ui::DragDropTypes::DRAG_NONE),
379 on(false),
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.
389 int index;
391 // Drop constants.
392 int operation;
394 // If true, the user is dropping on a folder.
395 bool on;
397 // Type of button.
398 DropButtonType button_type;
401 // DropInfo -------------------------------------------------------------------
403 // Tracks drops on the BookmarkBarView.
405 struct BookmarkBarView::DropInfo {
406 DropInfo()
407 : valid(false),
408 is_menu_showing(false),
409 x(0),
410 y(0) {
413 // Whether the data is valid.
414 bool valid;
416 // If true, the menu is being shown.
417 bool is_menu_showing;
419 // Coordinates of the drag (in terms of the BookmarkBarView).
420 int x;
421 int y;
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,
439 int x,
440 int height,
441 int vertical_padding,
442 SkColor top_color,
443 SkColor middle_color,
444 SkColor bottom_color) {
445 // Draw the upper half of the divider.
446 SkPaint paint;
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.
457 SkPaint paint_down;
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 {
469 public:
470 ButtonSeparatorView() {}
471 ~ButtonSeparatorView() override {}
473 void OnPaint(gfx::Canvas* canvas) override {
474 PaintVerticalDivider(
475 canvas,
476 kSeparatorStartX,
477 height(),
479 kEdgeDividerColor,
480 kMiddleDividerColor,
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;
495 private:
496 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
499 // BookmarkBarView ------------------------------------------------------------
501 // static
502 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView";
504 BookmarkBarView::BookmarkBarView(Browser* browser, BrowserView* browser_view)
505 : page_navigator_(NULL),
506 managed_(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),
514 instructions_(NULL),
515 bookmarks_separator_view_(NULL),
516 browser_(browser),
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);
524 Init();
526 size_animation_->Reset(1);
529 BookmarkBarView::~BookmarkBarView() {
530 if (model_)
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();
546 // static
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;
561 if (bookmark_menu_)
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();
576 else
577 size_animation_->Hide();
578 } else {
579 size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0);
581 bookmark_bar_state_ = state;
584 bool BookmarkBarView::is_animating() {
585 return size_animation_->is_animating();
588 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
589 const gfx::Point& loc,
590 int* model_start_index) {
591 *model_start_index = 0;
593 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
594 return NULL;
596 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
598 // Check the managed button first.
599 if (managed_bookmarks_button_->visible() &&
600 managed_bookmarks_button_->bounds().Contains(adjusted_loc)) {
601 return managed_->managed_node();
604 // Then check the supervised button.
605 if (supervised_bookmarks_button_->visible() &&
606 supervised_bookmarks_button_->bounds().Contains(adjusted_loc)) {
607 return managed_->supervised_node();
610 // Then check the bookmark buttons.
611 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
612 views::View* child = child_at(i);
613 if (!child->visible())
614 break;
615 if (child->bounds().Contains(adjusted_loc))
616 return model_->bookmark_bar_node()->GetChild(i);
619 // Then the overflow button.
620 if (overflow_button_->visible() &&
621 overflow_button_->bounds().Contains(adjusted_loc)) {
622 *model_start_index = GetFirstHiddenNodeIndex();
623 return model_->bookmark_bar_node();
626 // And finally the other folder.
627 if (other_bookmarks_button_->visible() &&
628 other_bookmarks_button_->bounds().Contains(adjusted_loc)) {
629 return model_->other_node();
632 return NULL;
635 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
636 const BookmarkNode* node) {
637 if (node == managed_->managed_node())
638 return managed_bookmarks_button_;
639 if (node == managed_->supervised_node())
640 return supervised_bookmarks_button_;
641 if (node == model_->other_node())
642 return other_bookmarks_button_;
643 if (node == model_->bookmark_bar_node())
644 return overflow_button_;
645 int index = model_->bookmark_bar_node()->GetIndexOf(node);
646 if (index == -1 || !node->is_folder())
647 return NULL;
648 return static_cast<views::MenuButton*>(child_at(index));
651 void BookmarkBarView::GetAnchorPositionForButton(
652 views::MenuButton* button,
653 views::MenuAnchorPosition* anchor) {
654 if (button == other_bookmarks_button_ || button == overflow_button_)
655 *anchor = views::MENU_ANCHOR_TOPRIGHT;
656 else
657 *anchor = views::MENU_ANCHOR_TOPLEFT;
660 views::MenuItemView* BookmarkBarView::GetMenu() {
661 return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
664 views::MenuItemView* BookmarkBarView::GetContextMenu() {
665 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
668 views::MenuItemView* BookmarkBarView::GetDropMenu() {
669 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
672 void BookmarkBarView::StopThrobbing(bool immediate) {
673 if (!throbbing_view_)
674 return;
676 // If not immediate, cycle through 2 more complete cycles.
677 throbbing_view_->StartThrobbing(immediate ? 0 : 4);
678 throbbing_view_ = NULL;
681 // static
682 base::string16 BookmarkBarView::CreateToolTipForURLAndTitle(
683 const views::Widget* widget,
684 const gfx::Point& screen_loc,
685 const GURL& url,
686 const base::string16& title,
687 Profile* profile) {
688 const views::TooltipManager* tooltip_manager = widget->GetTooltipManager();
689 int max_width = tooltip_manager->GetMaxWidth(screen_loc,
690 widget->GetNativeView());
691 const gfx::FontList tt_fonts = tooltip_manager->GetFontList();
692 base::string16 result;
694 // First the title.
695 if (!title.empty()) {
696 base::string16 localized_title = title;
697 base::i18n::AdjustStringForLocaleDirection(&localized_title);
698 result.append(gfx::ElideText(localized_title, tt_fonts, max_width,
699 gfx::ELIDE_TAIL));
702 // Only show the URL if the url and title differ.
703 if (title != base::UTF8ToUTF16(url.spec())) {
704 if (!result.empty())
705 result.push_back('\n');
707 // We need to explicitly specify the directionality of the URL's text to
708 // make sure it is treated as an LTR string when the context is RTL. For
709 // example, the URL "http://www.yahoo.com/" appears as
710 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
711 // the Unicode BiDi algorithm puts certain characters on the left by
712 // default.
713 std::string languages = profile->GetPrefs()->GetString(
714 prefs::kAcceptLanguages);
715 base::string16 elided_url(
716 url_formatter::ElideUrl(url, tt_fonts, max_width, languages));
717 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
718 result.append(elided_url);
720 return result;
723 bool BookmarkBarView::IsDetached() const {
724 return (bookmark_bar_state_ == BookmarkBar::DETACHED) ||
725 (animating_detached_ && size_animation_->is_animating());
728 double BookmarkBarView::GetAnimationValue() const {
729 return size_animation_->GetCurrentValue();
732 int BookmarkBarView::GetToolbarOverlap() const {
733 int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
734 views::NonClientFrameView::kClientEdgeThickness;
735 if (!IsDetached())
736 return attached_overlap;
738 int detached_overlap = views::NonClientFrameView::kClientEdgeThickness;
740 // Do not animate the overlap when the infobar is above us (i.e. when we're
741 // detached), since drawing over the infobar looks weird.
742 if (infobar_visible_)
743 return detached_overlap;
745 // When detached with no infobar, animate the overlap between the attached and
746 // detached states.
747 return detached_overlap + static_cast<int>(
748 (attached_overlap - detached_overlap) *
749 size_animation_->GetCurrentValue());
752 gfx::Size BookmarkBarView::GetPreferredSize() const {
753 gfx::Size prefsize;
754 if (IsDetached()) {
755 prefsize.set_height(
756 chrome::kBookmarkBarHeight +
757 static_cast<int>(
758 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
759 (1 - size_animation_->GetCurrentValue())));
760 } else {
761 prefsize.set_height(static_cast<int>(chrome::kBookmarkBarHeight *
762 size_animation_->GetCurrentValue()));
764 return prefsize;
767 bool BookmarkBarView::CanProcessEventsWithinSubtree() const {
768 // If the bookmark bar is attached and the omnibox popup is open (on top of
769 // the bar), prevent events from targeting the bookmark bar or any of its
770 // descendants. This will prevent hovers/clicks just above the omnibox popup
771 // from activating the top few pixels of items on the bookmark bar.
772 if (!IsDetached() && browser_view_ &&
773 browser_view_->GetLocationBar()->GetOmniboxView()->model()->
774 popup_model()->IsOpen()) {
775 return false;
777 return true;
780 gfx::Size BookmarkBarView::GetMinimumSize() const {
781 // The minimum width of the bookmark bar should at least contain the overflow
782 // button, by which one can access all the Bookmark Bar items, and the "Other
783 // Bookmarks" folder, along with appropriate margins and button padding.
784 // It should also contain the Managed and/or Supervised Bookmarks folders,
785 // if they are visible.
786 int width = kLeftMargin;
788 int height = chrome::kBookmarkBarHeight;
789 if (IsDetached()) {
790 double current_state = 1 - size_animation_->GetCurrentValue();
791 width += 2 * static_cast<int>(kNewTabHorizontalPadding * current_state);
792 height += static_cast<int>(
793 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
794 current_state);
797 if (managed_bookmarks_button_->visible()) {
798 gfx::Size size = managed_bookmarks_button_->GetPreferredSize();
799 width += size.width() + kButtonPadding;
801 if (supervised_bookmarks_button_->visible()) {
802 gfx::Size size = supervised_bookmarks_button_->GetPreferredSize();
803 width += size.width() + kButtonPadding;
805 if (other_bookmarks_button_->visible()) {
806 gfx::Size size = other_bookmarks_button_->GetPreferredSize();
807 width += size.width() + kButtonPadding;
809 if (overflow_button_->visible()) {
810 gfx::Size size = overflow_button_->GetPreferredSize();
811 width += size.width() + kButtonPadding;
813 if (bookmarks_separator_view_->visible()) {
814 gfx::Size size = bookmarks_separator_view_->GetPreferredSize();
815 width += size.width();
817 if (apps_page_shortcut_->visible()) {
818 gfx::Size size = apps_page_shortcut_->GetPreferredSize();
819 width += size.width() + kButtonPadding;
822 return gfx::Size(width, height);
825 void BookmarkBarView::Layout() {
826 // Skip layout during destruction, when no model exists.
827 if (!model_)
828 return;
830 int x = kLeftMargin;
831 int top_margin = IsDetached() ? kDetachedTopMargin : 0;
832 int y = top_margin;
833 int width = View::width() - kRightMargin - kLeftMargin;
834 int height = chrome::kBookmarkBarHeight - kBottomMargin;
835 int separator_margin = kSeparatorMargin;
837 if (IsDetached()) {
838 double current_state = 1 - size_animation_->GetCurrentValue();
839 x += static_cast<int>(kNewTabHorizontalPadding * current_state);
840 y += (View::height() - chrome::kBookmarkBarHeight) / 2;
841 width -= static_cast<int>(kNewTabHorizontalPadding * current_state);
842 separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
843 } else {
844 // For the attached appearance, pin the content to the bottom of the bar
845 // when animating in/out, as shrinking its height instead looks weird. This
846 // also matches how we layout infobars.
847 y += View::height() - chrome::kBookmarkBarHeight;
850 gfx::Size other_bookmarks_pref = other_bookmarks_button_->visible() ?
851 other_bookmarks_button_->GetPreferredSize() : gfx::Size();
852 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
853 gfx::Size bookmarks_separator_pref =
854 bookmarks_separator_view_->GetPreferredSize();
855 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ?
856 apps_page_shortcut_->GetPreferredSize() : gfx::Size();
858 int max_x = width - overflow_pref.width() - kButtonPadding -
859 bookmarks_separator_pref.width();
860 if (other_bookmarks_button_->visible())
861 max_x -= other_bookmarks_pref.width() + kButtonPadding;
863 // Start with the apps page shortcut button.
864 if (apps_page_shortcut_->visible()) {
865 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(),
866 height);
867 x += apps_page_shortcut_pref.width() + kButtonPadding;
870 // Then comes the managed bookmarks folder, if visible.
871 if (managed_bookmarks_button_->visible()) {
872 gfx::Size managed_bookmarks_pref =
873 managed_bookmarks_button_->GetPreferredSize();
874 managed_bookmarks_button_->SetBounds(x, y, managed_bookmarks_pref.width(),
875 height);
876 x += managed_bookmarks_pref.width() + kButtonPadding;
879 // Then the supervised bookmarks folder, if visible.
880 if (supervised_bookmarks_button_->visible()) {
881 gfx::Size supervised_bookmarks_pref =
882 supervised_bookmarks_button_->GetPreferredSize();
883 supervised_bookmarks_button_->SetBounds(
884 x, y, supervised_bookmarks_pref.width(), height);
885 x += supervised_bookmarks_pref.width() + kButtonPadding;
888 const bool show_instructions =
889 model_ && model_->loaded() &&
890 model_->bookmark_bar_node()->child_count() == 0;
891 instructions_->SetVisible(show_instructions);
892 if (show_instructions) {
893 gfx::Size pref = instructions_->GetPreferredSize();
894 instructions_->SetBounds(
895 x + kInstructionsPadding, y,
896 std::min(static_cast<int>(pref.width()),
897 max_x - x),
898 height);
899 } else {
900 bool last_visible = x < max_x;
901 int button_count = GetBookmarkButtonCount();
902 for (int i = 0; i <= button_count; ++i) {
903 if (i == button_count) {
904 // Add another button if there is room for it (and there is another
905 // button to load).
906 if (!last_visible || !model_->loaded() ||
907 model_->bookmark_bar_node()->child_count() <= button_count)
908 break;
909 AddChildViewAt(
910 CreateBookmarkButton(model_->bookmark_bar_node()->GetChild(i)), i);
911 button_count = GetBookmarkButtonCount();
913 views::View* child = child_at(i);
914 gfx::Size pref = child->GetPreferredSize();
915 int next_x = x + pref.width() + kButtonPadding;
916 last_visible = next_x < max_x;
917 child->SetVisible(last_visible);
918 // Only need to set bounds if the view is actually visible.
919 if (last_visible)
920 child->SetBounds(x, y, pref.width(), height);
921 x = next_x;
925 // Layout the right side buttons.
926 x = max_x + kButtonPadding;
928 // The overflow button.
929 overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
930 const bool show_overflow =
931 model_->loaded() &&
932 (model_->bookmark_bar_node()->child_count() > GetBookmarkButtonCount() ||
933 (GetBookmarkButtonCount() > 0 &&
934 !GetBookmarkButton(GetBookmarkButtonCount() - 1)->visible()));
935 overflow_button_->SetVisible(show_overflow);
936 x += overflow_pref.width();
938 // Separator.
939 if (bookmarks_separator_view_->visible()) {
940 bookmarks_separator_view_->SetBounds(x,
941 y - top_margin,
942 bookmarks_separator_pref.width(),
943 height + top_margin + kBottomMargin -
944 separator_margin);
946 x += bookmarks_separator_pref.width();
949 // The "Other Bookmarks" button.
950 if (other_bookmarks_button_->visible()) {
951 other_bookmarks_button_->SetBounds(x, y, other_bookmarks_pref.width(),
952 height);
953 x += other_bookmarks_pref.width() + kButtonPadding;
957 void BookmarkBarView::ViewHierarchyChanged(
958 const ViewHierarchyChangedDetails& details) {
959 if (details.is_add && details.child == this) {
960 // We may get inserted into a hierarchy with a profile - this typically
961 // occurs when the bar's contents get populated fast enough that the
962 // buttons are created before the bar is attached to a frame.
963 UpdateColors();
965 if (height() > 0) {
966 // We only layout while parented. When we become parented, if our bounds
967 // haven't changed, OnBoundsChanged() won't get invoked and we won't
968 // layout. Therefore we always force a layout when added.
969 Layout();
974 void BookmarkBarView::PaintChildren(const ui::PaintContext& context) {
975 View::PaintChildren(context);
977 if (drop_info_.get() && drop_info_->valid &&
978 drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
979 drop_info_->location.button_type != DROP_OVERFLOW &&
980 !drop_info_->location.on) {
981 int index = drop_info_->location.index;
982 DCHECK(index <= GetBookmarkButtonCount());
983 int x = 0;
984 int y = 0;
985 int h = height();
986 if (index == GetBookmarkButtonCount()) {
987 if (index == 0) {
988 x = kLeftMargin;
989 } else {
990 x = GetBookmarkButton(index - 1)->x() +
991 GetBookmarkButton(index - 1)->width();
993 } else {
994 x = GetBookmarkButton(index)->x();
996 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
997 y = GetBookmarkButton(0)->y();
998 h = GetBookmarkButton(0)->height();
1001 // Since the drop indicator is painted directly onto the canvas, we must
1002 // make sure it is painted in the right location if the locale is RTL.
1003 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
1005 kDropIndicatorWidth,
1007 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
1009 ui::PaintRecorder recorder(context, size());
1010 // TODO(sky/glen): make me pretty!
1011 recorder.canvas()->FillRect(indicator_bounds, kDropIndicatorColor);
1015 bool BookmarkBarView::GetDropFormats(
1016 int* formats,
1017 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
1018 if (!model_ || !model_->loaded())
1019 return false;
1020 *formats = ui::OSExchangeData::URL;
1021 custom_formats->insert(BookmarkNodeData::GetBookmarkFormatType());
1022 return true;
1025 bool BookmarkBarView::AreDropTypesRequired() {
1026 return true;
1029 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
1030 if (!model_ || !model_->loaded() ||
1031 !browser_->profile()->GetPrefs()->GetBoolean(
1032 bookmarks::prefs::kEditBookmarksEnabled))
1033 return false;
1035 if (!drop_info_.get())
1036 drop_info_.reset(new DropInfo());
1038 // Only accept drops of 1 node, which is the case for all data dragged from
1039 // bookmark bar and menus.
1040 return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
1043 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
1046 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
1047 if (!drop_info_.get())
1048 return 0;
1050 if (drop_info_->valid &&
1051 (drop_info_->x == event.x() && drop_info_->y == event.y())) {
1052 // The location of the mouse didn't change, return the last operation.
1053 return drop_info_->location.operation;
1056 drop_info_->x = event.x();
1057 drop_info_->y = event.y();
1059 DropLocation location;
1060 CalculateDropLocation(event, drop_info_->data, &location);
1062 if (drop_info_->valid && drop_info_->location.Equals(location)) {
1063 // The position we're going to drop didn't change, return the last drag
1064 // operation we calculated. Copy of the operation in case it changed.
1065 drop_info_->location.operation = location.operation;
1066 return drop_info_->location.operation;
1069 StopShowFolderDropMenuTimer();
1071 // TODO(sky): Optimize paint region.
1072 SchedulePaint();
1074 drop_info_->location = location;
1075 drop_info_->valid = true;
1077 if (drop_info_->is_menu_showing) {
1078 if (bookmark_drop_menu_)
1079 bookmark_drop_menu_->Cancel();
1080 drop_info_->is_menu_showing = false;
1083 if (location.on || location.button_type == DROP_OVERFLOW ||
1084 location.button_type == DROP_OTHER_FOLDER) {
1085 const BookmarkNode* node;
1086 if (location.button_type == DROP_OTHER_FOLDER)
1087 node = model_->other_node();
1088 else if (location.button_type == DROP_OVERFLOW)
1089 node = model_->bookmark_bar_node();
1090 else
1091 node = model_->bookmark_bar_node()->GetChild(location.index);
1092 StartShowFolderDropMenuTimer(node);
1095 return drop_info_->location.operation;
1098 void BookmarkBarView::OnDragExited() {
1099 StopShowFolderDropMenuTimer();
1101 // NOTE: we don't hide the menu on exit as it's possible the user moved the
1102 // mouse over the menu, which triggers an exit on us.
1104 drop_info_->valid = false;
1106 if (drop_info_->location.index != -1) {
1107 // TODO(sky): optimize the paint region.
1108 SchedulePaint();
1110 drop_info_.reset();
1113 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
1114 StopShowFolderDropMenuTimer();
1116 if (bookmark_drop_menu_)
1117 bookmark_drop_menu_->Cancel();
1119 if (!drop_info_.get() || !drop_info_->location.operation)
1120 return ui::DragDropTypes::DRAG_NONE;
1122 const BookmarkNode* root =
1123 (drop_info_->location.button_type == DROP_OTHER_FOLDER) ?
1124 model_->other_node() : model_->bookmark_bar_node();
1125 int index = drop_info_->location.index;
1127 if (index != -1) {
1128 // TODO(sky): optimize the SchedulePaint region.
1129 SchedulePaint();
1131 const BookmarkNode* parent_node;
1132 if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
1133 parent_node = root;
1134 index = parent_node->child_count();
1135 } else if (drop_info_->location.on) {
1136 parent_node = root->GetChild(index);
1137 index = parent_node->child_count();
1138 } else {
1139 parent_node = root;
1141 const BookmarkNodeData data = drop_info_->data;
1142 DCHECK(data.is_valid());
1143 bool copy = drop_info_->location.operation == ui::DragDropTypes::DRAG_COPY;
1144 drop_info_.reset();
1145 return chrome::DropBookmarks(
1146 browser_->profile(), data, parent_node, index, copy);
1149 void BookmarkBarView::OnThemeChanged() {
1150 UpdateColors();
1153 const char* BookmarkBarView::GetClassName() const {
1154 return kViewClassName;
1157 void BookmarkBarView::SetVisible(bool v) {
1158 if (v == visible())
1159 return;
1161 View::SetVisible(v);
1162 FOR_EACH_OBSERVER(BookmarkBarViewObserver, observers_,
1163 OnBookmarkBarVisibilityChanged());
1166 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) {
1167 state->role = ui::AX_ROLE_TOOLBAR;
1168 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
1171 void BookmarkBarView::AnimationProgressed(const gfx::Animation* animation) {
1172 // |browser_view_| can be NULL during tests.
1173 if (browser_view_)
1174 browser_view_->ToolbarSizeChanged(true);
1177 void BookmarkBarView::AnimationEnded(const gfx::Animation* animation) {
1178 // |browser_view_| can be NULL during tests.
1179 if (browser_view_) {
1180 browser_view_->ToolbarSizeChanged(false);
1181 SchedulePaint();
1185 void BookmarkBarView::BookmarkMenuControllerDeleted(
1186 BookmarkMenuController* controller) {
1187 if (controller == bookmark_menu_)
1188 bookmark_menu_ = NULL;
1189 else if (controller == bookmark_drop_menu_)
1190 bookmark_drop_menu_ = NULL;
1193 void BookmarkBarView::OnImportBookmarks() {
1194 int64 install_time = g_browser_process->metrics_service()->GetInstallDate();
1195 int64 time_from_install = base::Time::Now().ToTimeT() - install_time;
1196 if (bookmark_bar_state_ == BookmarkBar::SHOW) {
1197 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
1198 time_from_install);
1199 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) {
1200 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1201 time_from_install);
1204 chrome::ShowImportDialog(browser_);
1207 void BookmarkBarView::OnBookmarkBubbleShown(const BookmarkNode* node) {
1208 StopThrobbing(true);
1209 if (!node)
1210 return; // Generally shouldn't happen.
1211 StartThrobbing(node, false);
1214 void BookmarkBarView::OnBookmarkBubbleHidden() {
1215 StopThrobbing(false);
1218 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model,
1219 bool ids_reassigned) {
1220 // There should be no buttons. If non-zero it means Load was invoked more than
1221 // once, or we didn't properly clear things. Either of which shouldn't happen.
1222 // The actual bookmark buttons are added from Layout().
1223 DCHECK_EQ(0, GetBookmarkButtonCount());
1224 DCHECK(model->other_node());
1225 other_bookmarks_button_->SetAccessibleName(model->other_node()->GetTitle());
1226 other_bookmarks_button_->SetText(model->other_node()->GetTitle());
1227 managed_bookmarks_button_->SetAccessibleName(
1228 managed_->managed_node()->GetTitle());
1229 managed_bookmarks_button_->SetText(managed_->managed_node()->GetTitle());
1230 supervised_bookmarks_button_->SetAccessibleName(
1231 managed_->supervised_node()->GetTitle());
1232 supervised_bookmarks_button_->SetText(
1233 managed_->supervised_node()->GetTitle());
1234 UpdateColors();
1235 UpdateOtherAndManagedButtonsVisibility();
1236 other_bookmarks_button_->SetEnabled(true);
1237 managed_bookmarks_button_->SetEnabled(true);
1238 supervised_bookmarks_button_->SetEnabled(true);
1239 LayoutAndPaint();
1242 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
1243 NOTREACHED();
1244 // Do minimal cleanup, presumably we'll be deleted shortly.
1245 model_->RemoveObserver(this);
1246 model_ = NULL;
1249 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
1250 const BookmarkNode* old_parent,
1251 int old_index,
1252 const BookmarkNode* new_parent,
1253 int new_index) {
1254 bool was_throbbing = throbbing_view_ &&
1255 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
1256 if (was_throbbing)
1257 throbbing_view_->StopThrobbing();
1258 bool needs_layout_and_paint =
1259 BookmarkNodeRemovedImpl(model, old_parent, old_index);
1260 if (BookmarkNodeAddedImpl(model, new_parent, new_index))
1261 needs_layout_and_paint = true;
1262 if (was_throbbing && new_index < GetBookmarkButtonCount())
1263 StartThrobbing(new_parent->GetChild(new_index), false);
1264 if (needs_layout_and_paint)
1265 LayoutAndPaint();
1268 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
1269 const BookmarkNode* parent,
1270 int index) {
1271 if (BookmarkNodeAddedImpl(model, parent, index))
1272 LayoutAndPaint();
1275 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1276 const BookmarkNode* parent,
1277 int old_index,
1278 const BookmarkNode* node,
1279 const std::set<GURL>& removed_urls) {
1280 // Close the menu if the menu is showing for the deleted node.
1281 if (bookmark_menu_ && bookmark_menu_->node() == node)
1282 bookmark_menu_->Cancel();
1283 if (BookmarkNodeRemovedImpl(model, parent, old_index))
1284 LayoutAndPaint();
1287 void BookmarkBarView::BookmarkAllUserNodesRemoved(
1288 BookmarkModel* model,
1289 const std::set<GURL>& removed_urls) {
1290 UpdateOtherAndManagedButtonsVisibility();
1292 StopThrobbing(true);
1294 // Remove the existing buttons.
1295 while (GetBookmarkButtonCount())
1296 delete GetBookmarkButton(0);
1298 LayoutAndPaint();
1301 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1302 const BookmarkNode* node) {
1303 BookmarkNodeChangedImpl(model, node);
1306 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1307 const BookmarkNode* node) {
1308 if (node != model->bookmark_bar_node())
1309 return; // We only care about reordering of the bookmark bar node.
1311 // Remove the existing buttons.
1312 while (GetBookmarkButtonCount())
1313 delete child_at(0);
1315 // Create the new buttons.
1316 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1317 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1319 LayoutAndPaint();
1322 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model,
1323 const BookmarkNode* node) {
1324 BookmarkNodeChangedImpl(model, node);
1327 void BookmarkBarView::WriteDragDataForView(View* sender,
1328 const gfx::Point& press_pt,
1329 ui::OSExchangeData* data) {
1330 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1332 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1333 if (sender == GetBookmarkButton(i)) {
1334 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1335 gfx::ImageSkia icon;
1336 if (node->is_url()) {
1337 const gfx::Image& image = model_->GetFavicon(node);
1338 icon = image.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1339 : image.AsImageSkia();
1340 } else {
1341 icon = chrome::GetBookmarkFolderIcon();
1344 button_drag_utils::SetDragImage(node->url(), node->GetTitle(), icon,
1345 &press_pt, data,
1346 GetBookmarkButton(i)->GetWidget());
1347 WriteBookmarkDragData(node, data);
1348 return;
1351 NOTREACHED();
1354 int BookmarkBarView::GetDragOperationsForView(View* sender,
1355 const gfx::Point& p) {
1356 if (size_animation_->is_animating() ||
1357 (size_animation_->GetCurrentValue() == 0 &&
1358 bookmark_bar_state_ != BookmarkBar::DETACHED)) {
1359 // Don't let the user drag while animating open or we're closed (and not
1360 // detached, when detached size_animation_ is always 0). This typically is
1361 // only hit if the user does something to inadvertently trigger DnD such as
1362 // pressing the mouse and hitting control-b.
1363 return ui::DragDropTypes::DRAG_NONE;
1366 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1367 if (sender == GetBookmarkButton(i)) {
1368 return chrome::GetBookmarkDragOperation(
1369 browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
1372 NOTREACHED();
1373 return ui::DragDropTypes::DRAG_NONE;
1376 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1377 const gfx::Point& press_pt,
1378 const gfx::Point& p) {
1379 // Check if we have not moved enough horizontally but we have moved downward
1380 // vertically - downward drag.
1381 gfx::Vector2d move_offset = p - press_pt;
1382 gfx::Vector2d horizontal_offset(move_offset.x(), 0);
1383 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
1384 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1385 if (sender == GetBookmarkButton(i)) {
1386 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1387 // If the folder button was dragged, show the menu instead.
1388 if (node && node->is_folder()) {
1389 views::MenuButton* menu_button =
1390 static_cast<views::MenuButton*>(sender);
1391 menu_button->Activate();
1392 return false;
1394 break;
1398 return true;
1401 void BookmarkBarView::OnMenuButtonClicked(views::View* view,
1402 const gfx::Point& point) {
1403 const BookmarkNode* node;
1405 int start_index = 0;
1406 if (view == other_bookmarks_button_) {
1407 node = model_->other_node();
1408 } else if (view == managed_bookmarks_button_) {
1409 node = managed_->managed_node();
1410 } else if (view == supervised_bookmarks_button_) {
1411 node = managed_->supervised_node();
1412 } else if (view == overflow_button_) {
1413 node = model_->bookmark_bar_node();
1414 start_index = GetFirstHiddenNodeIndex();
1415 } else {
1416 int button_index = GetIndexOf(view);
1417 DCHECK_NE(-1, button_index);
1418 node = model_->bookmark_bar_node()->GetChild(button_index);
1421 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1422 bookmark_menu_ = new BookmarkMenuController(
1423 browser_, page_navigator_, GetWidget(), node, start_index, false);
1424 bookmark_menu_->set_observer(this);
1425 bookmark_menu_->RunMenuAt(this);
1428 void BookmarkBarView::ButtonPressed(views::Button* sender,
1429 const ui::Event& event) {
1430 WindowOpenDisposition disposition_from_event_flags =
1431 ui::DispositionFromEventFlags(event.flags());
1433 if (sender->tag() == kAppsShortcutButtonTag) {
1434 OpenURLParams params(GURL(chrome::kChromeUIAppsURL),
1435 Referrer(),
1436 disposition_from_event_flags,
1437 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1438 false);
1439 page_navigator_->OpenURL(params);
1440 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1441 return;
1444 const BookmarkNode* node;
1445 if (sender->tag() == kOtherFolderButtonTag) {
1446 node = model_->other_node();
1447 } else if (sender->tag() == kManagedFolderButtonTag) {
1448 node = managed_->managed_node();
1449 } else if (sender->tag() == kSupervisedFolderButtonTag) {
1450 node = managed_->supervised_node();
1451 } else {
1452 int index = GetIndexOf(sender);
1453 DCHECK_NE(-1, index);
1454 node = model_->bookmark_bar_node()->GetChild(index);
1456 DCHECK(page_navigator_);
1458 if (node->is_url()) {
1459 RecordAppLaunch(browser_->profile(), node->url());
1460 OpenURLParams params(
1461 node->url(), Referrer(), disposition_from_event_flags,
1462 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
1463 page_navigator_->OpenURL(params);
1464 } else {
1465 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node,
1466 disposition_from_event_flags, browser_->profile());
1469 RecordBookmarkLaunch(node, GetBookmarkLaunchLocation());
1472 void BookmarkBarView::ShowContextMenuForView(views::View* source,
1473 const gfx::Point& point,
1474 ui::MenuSourceType source_type) {
1475 if (!model_->loaded()) {
1476 // Don't do anything if the model isn't loaded.
1477 return;
1480 const BookmarkNode* parent = NULL;
1481 std::vector<const BookmarkNode*> nodes;
1482 if (source == other_bookmarks_button_) {
1483 parent = model_->other_node();
1484 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1485 // sure the user can't edit/delete the node in this case.
1486 nodes.push_back(parent);
1487 } else if (source == managed_bookmarks_button_) {
1488 parent = managed_->managed_node();
1489 nodes.push_back(parent);
1490 } else if (source == supervised_bookmarks_button_) {
1491 parent = managed_->supervised_node();
1492 nodes.push_back(parent);
1493 } else if (source != this && source != apps_page_shortcut_) {
1494 // User clicked on one of the bookmark buttons, find which one they
1495 // clicked on, except for the apps page shortcut, which must behave as if
1496 // the user clicked on the bookmark bar background.
1497 int bookmark_button_index = GetIndexOf(source);
1498 DCHECK(bookmark_button_index != -1 &&
1499 bookmark_button_index < GetBookmarkButtonCount());
1500 const BookmarkNode* node =
1501 model_->bookmark_bar_node()->GetChild(bookmark_button_index);
1502 nodes.push_back(node);
1503 parent = node->parent();
1504 } else {
1505 parent = model_->bookmark_bar_node();
1506 nodes.push_back(parent);
1508 bool close_on_remove =
1509 (parent == model_->other_node()) && (parent->child_count() == 1);
1511 context_menu_.reset(new BookmarkContextMenu(
1512 GetWidget(), browser_, browser_->profile(),
1513 browser_->tab_strip_model()->GetActiveWebContents(),
1514 parent, nodes, close_on_remove));
1515 context_menu_->RunMenuAt(point, source_type);
1518 void BookmarkBarView::Init() {
1519 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1520 // return NULL. When we're inserted into a hierarchy, we'll call
1521 // UpdateColors(), which will set the appropriate colors for all the objects
1522 // added in this function.
1524 // Child views are traversed in the order they are added. Make sure the order
1525 // they are added matches the visual order.
1526 overflow_button_ = CreateOverflowButton();
1527 AddChildView(overflow_button_);
1529 other_bookmarks_button_ = CreateOtherBookmarksButton();
1530 // We'll re-enable when the model is loaded.
1531 other_bookmarks_button_->SetEnabled(false);
1532 AddChildView(other_bookmarks_button_);
1534 managed_bookmarks_button_ = CreateManagedBookmarksButton();
1535 // Also re-enabled when the model is loaded.
1536 managed_bookmarks_button_->SetEnabled(false);
1537 AddChildView(managed_bookmarks_button_);
1539 supervised_bookmarks_button_ = CreateSupervisedBookmarksButton();
1540 // Also re-enabled when the model is loaded.
1541 supervised_bookmarks_button_->SetEnabled(false);
1542 AddChildView(supervised_bookmarks_button_);
1544 apps_page_shortcut_ = CreateAppsPageShortcutButton();
1545 AddChildView(apps_page_shortcut_);
1546 profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
1547 profile_pref_registrar_.Add(
1548 bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1549 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged,
1550 base::Unretained(this)));
1551 profile_pref_registrar_.Add(
1552 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
1553 base::Bind(&BookmarkBarView::OnShowManagedBookmarksPrefChanged,
1554 base::Unretained(this)));
1555 apps_page_shortcut_->SetVisible(
1556 chrome::ShouldShowAppsShortcutInBookmarkBar(
1557 browser_->profile(), browser_->host_desktop_type()));
1559 bookmarks_separator_view_ = new ButtonSeparatorView();
1560 AddChildView(bookmarks_separator_view_);
1561 UpdateBookmarksSeparatorVisibility();
1563 instructions_ = new BookmarkBarInstructionsView(this);
1564 AddChildView(instructions_);
1566 set_context_menu_controller(this);
1568 size_animation_.reset(new gfx::SlideAnimation(this));
1570 model_ = BookmarkModelFactory::GetForProfile(browser_->profile());
1571 managed_ = ManagedBookmarkServiceFactory::GetForProfile(browser_->profile());
1572 if (model_) {
1573 model_->AddObserver(this);
1574 if (model_->loaded())
1575 BookmarkModelLoaded(model_, false);
1576 // else case: we'll receive notification back from the BookmarkModel when
1577 // done loading, then we'll populate the bar.
1581 int BookmarkBarView::GetBookmarkButtonCount() const {
1582 // We contain seven non-bookmark button views: managed bookmarks, supervised
1583 // bookmarks, other bookmarks, bookmarks separator, chevrons (for overflow),
1584 // apps page, and the instruction label.
1585 return child_count() - 7;
1588 views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) {
1589 // CHECK as otherwise we may do the wrong cast.
1590 CHECK(index >= 0 && index < GetBookmarkButtonCount());
1591 return static_cast<views::LabelButton*>(child_at(index));
1594 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
1595 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
1596 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
1599 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1600 const int bb_count = GetBookmarkButtonCount();
1601 for (int i = 0; i < bb_count; ++i) {
1602 if (!GetBookmarkButton(i)->visible())
1603 return i;
1605 return bb_count;
1608 MenuButton* BookmarkBarView::CreateOtherBookmarksButton() {
1609 // Title is set in Loaded.
1610 MenuButton* button =
1611 new BookmarkFolderButton(this, base::string16(), this, false);
1612 button->set_id(VIEW_ID_OTHER_BOOKMARKS);
1613 button->SetImage(views::Button::STATE_NORMAL,
1614 chrome::GetBookmarkFolderIcon());
1615 button->set_context_menu_controller(this);
1616 button->set_tag(kOtherFolderButtonTag);
1617 return button;
1620 MenuButton* BookmarkBarView::CreateManagedBookmarksButton() {
1621 // Title is set in Loaded.
1622 MenuButton* button =
1623 new BookmarkFolderButton(this, base::string16(), this, false);
1624 button->set_id(VIEW_ID_MANAGED_BOOKMARKS);
1625 button->SetImage(views::Button::STATE_NORMAL,
1626 chrome::GetBookmarkManagedFolderIcon());
1627 button->set_context_menu_controller(this);
1628 button->set_tag(kManagedFolderButtonTag);
1629 return button;
1632 MenuButton* BookmarkBarView::CreateSupervisedBookmarksButton() {
1633 // Title is set in Loaded.
1634 MenuButton* button =
1635 new BookmarkFolderButton(this, base::string16(), this, false);
1636 button->set_id(VIEW_ID_SUPERVISED_BOOKMARKS);
1637 button->SetImage(views::Button::STATE_NORMAL,
1638 chrome::GetBookmarkSupervisedFolderIcon());
1639 button->set_context_menu_controller(this);
1640 button->set_tag(kSupervisedFolderButtonTag);
1641 return button;
1644 MenuButton* BookmarkBarView::CreateOverflowButton() {
1645 MenuButton* button = new OverFlowButton(this);
1646 button->SetImage(views::Button::STATE_NORMAL,
1647 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS));
1649 // The overflow button's image contains an arrow and therefore it is a
1650 // direction sensitive image and we need to flip it if the UI layout is
1651 // right-to-left.
1653 // By default, menu buttons are not flipped because they generally contain
1654 // text and flipping the gfx::Canvas object will break text rendering. Since
1655 // the overflow button does not contain text, we can safely flip it.
1656 button->EnableCanvasFlippingForRTLUI(true);
1658 // Make visible as necessary.
1659 button->SetVisible(false);
1660 // Set accessibility name.
1661 button->SetAccessibleName(
1662 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
1663 return button;
1666 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1667 if (node->is_url()) {
1668 BookmarkButton* button = new BookmarkButton(
1669 this, node->url(), node->GetTitle(), browser_->profile());
1670 ConfigureButton(node, button);
1671 return button;
1673 views::MenuButton* button =
1674 new BookmarkFolderButton(this, node->GetTitle(), this, false);
1675 button->SetImage(views::Button::STATE_NORMAL,
1676 chrome::GetBookmarkFolderIcon());
1677 ConfigureButton(node, button);
1678 return button;
1681 views::LabelButton* BookmarkBarView::CreateAppsPageShortcutButton() {
1682 views::LabelButton* button = new ShortcutButton(
1683 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME));
1684 button->SetTooltipText(l10n_util::GetStringUTF16(
1685 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP));
1686 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1687 button->SetImage(views::Button::STATE_NORMAL,
1688 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT));
1689 button->set_context_menu_controller(this);
1690 button->set_tag(kAppsShortcutButtonTag);
1691 return button;
1694 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1695 views::LabelButton* button) {
1696 button->SetText(node->GetTitle());
1697 button->SetAccessibleName(node->GetTitle());
1698 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1699 // We don't always have a theme provider (ui tests, for example).
1700 if (GetThemeProvider()) {
1701 button->SetTextColor(
1702 views::Button::STATE_NORMAL,
1703 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
1706 button->SetMinSize(gfx::Size());
1707 button->set_context_menu_controller(this);
1708 button->set_drag_controller(this);
1709 if (node->is_url()) {
1710 const gfx::Image& favicon = model_->GetFavicon(node);
1711 button->SetImage(views::Button::STATE_NORMAL,
1712 favicon.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1713 : *favicon.ToImageSkia());
1715 button->SetMaxSize(gfx::Size(kMaxButtonWidth, 0));
1718 bool BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
1719 const BookmarkNode* parent,
1720 int index) {
1721 const bool needs_layout_and_paint = UpdateOtherAndManagedButtonsVisibility();
1722 if (parent != model->bookmark_bar_node())
1723 return needs_layout_and_paint;
1724 if (index < GetBookmarkButtonCount()) {
1725 const BookmarkNode* node = parent->GetChild(index);
1726 AddChildViewAt(CreateBookmarkButton(node), index);
1727 return true;
1729 // If the new node was added after the last button we've created we may be
1730 // able to fit it. Assume we can by returning true, which forces a Layout()
1731 // and creation of the button (if it fits).
1732 return index == GetBookmarkButtonCount();
1735 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1736 const BookmarkNode* parent,
1737 int index) {
1738 const bool needs_layout = UpdateOtherAndManagedButtonsVisibility();
1740 StopThrobbing(true);
1741 // No need to start throbbing again as the bookmark bubble can't be up at
1742 // the same time as the user reorders.
1744 if (parent != model->bookmark_bar_node()) {
1745 // Only children of the bookmark_bar_node get buttons.
1746 return needs_layout;
1748 if (index >= GetBookmarkButtonCount())
1749 return needs_layout;
1751 delete child_at(index);
1752 return true;
1755 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1756 const BookmarkNode* node) {
1757 if (node == managed_->managed_node()) {
1758 // The managed node may have its title updated.
1759 managed_bookmarks_button_->SetAccessibleName(
1760 managed_->managed_node()->GetTitle());
1761 managed_bookmarks_button_->SetText(managed_->managed_node()->GetTitle());
1762 return;
1764 if (node == managed_->supervised_node()) {
1765 // The supervised node may have its title updated.
1766 supervised_bookmarks_button_->SetAccessibleName(
1767 managed_->supervised_node()->GetTitle());
1768 supervised_bookmarks_button_->SetText(
1769 managed_->supervised_node()->GetTitle());
1770 return;
1773 if (node->parent() != model->bookmark_bar_node()) {
1774 // We only care about nodes on the bookmark bar.
1775 return;
1777 int index = model->bookmark_bar_node()->GetIndexOf(node);
1778 DCHECK_NE(-1, index);
1779 if (index >= GetBookmarkButtonCount())
1780 return; // Buttons are created as needed.
1781 views::LabelButton* button = GetBookmarkButton(index);
1782 const int old_pref_width = button->GetPreferredSize().width();
1783 ConfigureButton(node, button);
1784 if (old_pref_width != button->GetPreferredSize().width())
1785 LayoutAndPaint();
1788 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1789 if (bookmark_drop_menu_) {
1790 if (bookmark_drop_menu_->node() == node) {
1791 // Already showing for the specified node.
1792 return;
1794 bookmark_drop_menu_->Cancel();
1797 views::MenuButton* menu_button = GetMenuButtonForNode(node);
1798 if (!menu_button)
1799 return;
1801 int start_index = 0;
1802 if (node == model_->bookmark_bar_node())
1803 start_index = GetFirstHiddenNodeIndex();
1805 drop_info_->is_menu_showing = true;
1806 bookmark_drop_menu_ = new BookmarkMenuController(
1807 browser_, page_navigator_, GetWidget(), node, start_index, true);
1808 bookmark_drop_menu_->set_observer(this);
1809 bookmark_drop_menu_->RunMenuAt(this);
1812 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1813 show_folder_method_factory_.InvalidateWeakPtrs();
1816 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1817 if (!animations_enabled) {
1818 // So that tests can run as fast as possible disable the delay during
1819 // testing.
1820 ShowDropFolderForNode(node);
1821 return;
1823 show_folder_method_factory_.InvalidateWeakPtrs();
1824 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1825 FROM_HERE, base::Bind(&BookmarkBarView::ShowDropFolderForNode,
1826 show_folder_method_factory_.GetWeakPtr(), node),
1827 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1830 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event,
1831 const BookmarkNodeData& data,
1832 DropLocation* location) {
1833 DCHECK(model_);
1834 DCHECK(model_->loaded());
1835 DCHECK(data.is_valid());
1837 *location = DropLocation();
1839 // The drop event uses the screen coordinates while the child Views are
1840 // always laid out from left to right (even though they are rendered from
1841 // right-to-left on RTL locales). Thus, in order to make sure the drop
1842 // coordinates calculation works, we mirror the event's X coordinate if the
1843 // locale is RTL.
1844 int mirrored_x = GetMirroredXInView(event.x());
1846 bool found = false;
1847 const int other_delta_x = mirrored_x - other_bookmarks_button_->x();
1848 Profile* profile = browser_->profile();
1849 if (other_bookmarks_button_->visible() && other_delta_x >= 0 &&
1850 other_delta_x < other_bookmarks_button_->width()) {
1851 // Mouse is over 'other' folder.
1852 location->button_type = DROP_OTHER_FOLDER;
1853 location->on = true;
1854 found = true;
1855 } else if (!GetBookmarkButtonCount()) {
1856 // No bookmarks, accept the drop.
1857 location->index = 0;
1858 const BookmarkNode* node = data.GetFirstNode(model_, profile->GetPath());
1859 int ops = node && managed_->CanBeEditedByUser(node) ?
1860 ui::DragDropTypes::DRAG_MOVE :
1861 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
1862 location->operation = chrome::GetPreferredBookmarkDropOperation(
1863 event.source_operations(), ops);
1864 return;
1867 for (int i = 0; i < GetBookmarkButtonCount() &&
1868 GetBookmarkButton(i)->visible() && !found; i++) {
1869 views::LabelButton* button = GetBookmarkButton(i);
1870 int button_x = mirrored_x - button->x();
1871 int button_w = button->width();
1872 if (button_x < button_w) {
1873 found = true;
1874 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1875 if (node->is_folder()) {
1876 if (button_x <= views::kDropBetweenPixels) {
1877 location->index = i;
1878 } else if (button_x < button_w - views::kDropBetweenPixels) {
1879 location->index = i;
1880 location->on = true;
1881 } else {
1882 location->index = i + 1;
1884 } else if (button_x < button_w / 2) {
1885 location->index = i;
1886 } else {
1887 location->index = i + 1;
1889 break;
1893 if (!found) {
1894 if (overflow_button_->visible()) {
1895 // Are we over the overflow button?
1896 int overflow_delta_x = mirrored_x - overflow_button_->x();
1897 if (overflow_delta_x >= 0 &&
1898 overflow_delta_x < overflow_button_->width()) {
1899 // Mouse is over overflow button.
1900 location->index = GetFirstHiddenNodeIndex();
1901 location->button_type = DROP_OVERFLOW;
1902 } else if (overflow_delta_x < 0) {
1903 // Mouse is after the last visible button but before overflow button;
1904 // use the last visible index.
1905 location->index = GetFirstHiddenNodeIndex();
1906 } else {
1907 return;
1909 } else if (!other_bookmarks_button_->visible() ||
1910 mirrored_x < other_bookmarks_button_->x()) {
1911 // Mouse is after the last visible button but before more recently
1912 // bookmarked; use the last visible index.
1913 location->index = GetFirstHiddenNodeIndex();
1914 } else {
1915 return;
1919 if (location->on) {
1920 const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ?
1921 model_->other_node() :
1922 model_->bookmark_bar_node()->GetChild(location->index);
1923 location->operation = chrome::GetBookmarkDropOperation(
1924 profile, event, data, parent, parent->child_count());
1925 if (!location->operation && !data.has_single_url() &&
1926 data.GetFirstNode(model_, profile->GetPath()) == parent) {
1927 // Don't open a menu if the node being dragged is the menu to open.
1928 location->on = false;
1930 } else {
1931 location->operation = chrome::GetBookmarkDropOperation(
1932 profile, event, data, model_->bookmark_bar_node(), location->index);
1936 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
1937 ui::OSExchangeData* data) {
1938 DCHECK(node && data);
1939 BookmarkNodeData drag_data(node);
1940 drag_data.Write(browser_->profile()->GetPath(), data);
1943 void BookmarkBarView::StartThrobbing(const BookmarkNode* node,
1944 bool overflow_only) {
1945 DCHECK(!throbbing_view_);
1947 // Determine which visible button is showing the bookmark (or is an ancestor
1948 // of the bookmark).
1949 const BookmarkNode* bbn = model_->bookmark_bar_node();
1950 const BookmarkNode* parent_on_bb = node;
1951 while (parent_on_bb) {
1952 const BookmarkNode* parent = parent_on_bb->parent();
1953 if (parent == bbn)
1954 break;
1955 parent_on_bb = parent;
1957 if (parent_on_bb) {
1958 int index = bbn->GetIndexOf(parent_on_bb);
1959 if (index >= GetFirstHiddenNodeIndex()) {
1960 // Node is hidden, animate the overflow button.
1961 throbbing_view_ = overflow_button_;
1962 } else if (!overflow_only) {
1963 throbbing_view_ = static_cast<CustomButton*>(child_at(index));
1965 } else if (bookmarks::IsDescendantOf(node, managed_->managed_node())) {
1966 throbbing_view_ = managed_bookmarks_button_;
1967 } else if (bookmarks::IsDescendantOf(node, managed_->supervised_node())) {
1968 throbbing_view_ = supervised_bookmarks_button_;
1969 } else if (!overflow_only) {
1970 throbbing_view_ = other_bookmarks_button_;
1973 // Use a large number so that the button continues to throb.
1974 if (throbbing_view_)
1975 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
1978 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove(
1979 const BookmarkNode* parent,
1980 int old_index) {
1981 const BookmarkNode* bbn = model_->bookmark_bar_node();
1982 const BookmarkNode* old_node = parent;
1983 int old_index_on_bb = old_index;
1984 while (old_node && old_node != bbn) {
1985 const BookmarkNode* parent = old_node->parent();
1986 if (parent == bbn) {
1987 old_index_on_bb = bbn->GetIndexOf(old_node);
1988 break;
1990 old_node = parent;
1992 if (old_node) {
1993 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) {
1994 // Node is hidden, animate the overflow button.
1995 return overflow_button_;
1997 return static_cast<CustomButton*>(child_at(old_index_on_bb));
1999 if (bookmarks::IsDescendantOf(parent, managed_->managed_node()))
2000 return managed_bookmarks_button_;
2001 if (bookmarks::IsDescendantOf(parent, managed_->supervised_node()))
2002 return supervised_bookmarks_button_;
2003 // Node wasn't on the bookmark bar, use the "Other Bookmarks" button.
2004 return other_bookmarks_button_;
2007 void BookmarkBarView::UpdateColors() {
2008 // We don't always have a theme provider (ui tests, for example).
2009 const ui::ThemeProvider* theme_provider = GetThemeProvider();
2010 if (!theme_provider)
2011 return;
2012 SkColor color =
2013 theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
2014 for (int i = 0; i < GetBookmarkButtonCount(); ++i)
2015 GetBookmarkButton(i)->SetTextColor(views::Button::STATE_NORMAL, color);
2016 other_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color);
2017 managed_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color);
2018 supervised_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL,
2019 color);
2020 if (apps_page_shortcut_->visible())
2021 apps_page_shortcut_->SetTextColor(views::Button::STATE_NORMAL, color);
2024 bool BookmarkBarView::UpdateOtherAndManagedButtonsVisibility() {
2025 bool has_other_children = !model_->other_node()->empty();
2026 bool update_other = has_other_children != other_bookmarks_button_->visible();
2027 if (update_other) {
2028 other_bookmarks_button_->SetVisible(has_other_children);
2029 UpdateBookmarksSeparatorVisibility();
2032 bool show_managed = !managed_->managed_node()->empty() &&
2033 browser_->profile()->GetPrefs()->GetBoolean(
2034 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
2035 bool update_managed = show_managed != managed_bookmarks_button_->visible();
2036 if (update_managed)
2037 managed_bookmarks_button_->SetVisible(show_managed);
2039 bool show_supervised = !managed_->supervised_node()->empty();
2040 bool update_supervised =
2041 show_supervised != supervised_bookmarks_button_->visible();
2042 if (update_supervised)
2043 supervised_bookmarks_button_->SetVisible(show_supervised);
2045 return update_other || update_managed || update_supervised;
2048 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() {
2049 // Ash does not paint the bookmarks separator line because it looks odd on
2050 // the flat background. We keep it present for layout, but don't draw it.
2051 bookmarks_separator_view_->SetVisible(
2052 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH &&
2053 other_bookmarks_button_->visible());
2056 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() {
2057 DCHECK(apps_page_shortcut_);
2058 // Only perform layout if required.
2059 bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar(
2060 browser_->profile(), browser_->host_desktop_type());
2061 if (apps_page_shortcut_->visible() == visible)
2062 return;
2063 apps_page_shortcut_->SetVisible(visible);
2064 UpdateBookmarksSeparatorVisibility();
2065 LayoutAndPaint();
2068 void BookmarkBarView::OnShowManagedBookmarksPrefChanged() {
2069 if (UpdateOtherAndManagedButtonsVisibility())
2070 LayoutAndPaint();