Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bar_view.cc
blobafe033a0cc66441cd4ed2115e8dcc2184f449da4
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/i18n/rtl.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
19 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
20 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/defaults.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/search/search.h"
26 #include "chrome/browser/sync/profile_sync_service.h"
27 #include "chrome/browser/sync/profile_sync_service_factory.h"
28 #include "chrome/browser/themes/theme_properties.h"
29 #include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
30 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
31 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
32 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
33 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/chrome_pages.h"
35 #include "chrome/browser/ui/elide_url.h"
36 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
37 #include "chrome/browser/ui/omnibox/omnibox_view.h"
38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
39 #include "chrome/browser/ui/view_ids.h"
40 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h"
41 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h"
42 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
43 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
44 #include "chrome/browser/ui/views/event_utils.h"
45 #include "chrome/browser/ui/views/frame/browser_view.h"
46 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
47 #include "chrome/common/chrome_switches.h"
48 #include "chrome/common/extensions/extension_constants.h"
49 #include "chrome/common/extensions/extension_metrics.h"
50 #include "chrome/common/pref_names.h"
51 #include "chrome/common/url_constants.h"
52 #include "chrome/grit/generated_resources.h"
53 #include "components/bookmarks/browser/bookmark_model.h"
54 #include "components/bookmarks/browser/bookmark_utils.h"
55 #include "components/metrics/metrics_service.h"
56 #include "content/public/browser/notification_details.h"
57 #include "content/public/browser/notification_source.h"
58 #include "content/public/browser/page_navigator.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/render_widget_host_view.h"
61 #include "content/public/browser/user_metrics.h"
62 #include "content/public/browser/web_contents.h"
63 #include "extensions/browser/extension_registry.h"
64 #include "extensions/common/extension.h"
65 #include "extensions/common/extension_set.h"
66 #include "grit/theme_resources.h"
67 #include "ui/accessibility/ax_view_state.h"
68 #include "ui/base/dragdrop/drag_utils.h"
69 #include "ui/base/dragdrop/os_exchange_data.h"
70 #include "ui/base/l10n/l10n_util.h"
71 #include "ui/base/page_transition_types.h"
72 #include "ui/base/resource/resource_bundle.h"
73 #include "ui/base/theme_provider.h"
74 #include "ui/base/window_open_disposition.h"
75 #include "ui/gfx/animation/slide_animation.h"
76 #include "ui/gfx/canvas.h"
77 #include "ui/gfx/text_constants.h"
78 #include "ui/gfx/text_elider.h"
79 #include "ui/resources/grit/ui_resources.h"
80 #include "ui/views/button_drag_utils.h"
81 #include "ui/views/controls/button/label_button.h"
82 #include "ui/views/controls/button/label_button_border.h"
83 #include "ui/views/controls/button/menu_button.h"
84 #include "ui/views/controls/label.h"
85 #include "ui/views/drag_utils.h"
86 #include "ui/views/metrics.h"
87 #include "ui/views/view_constants.h"
88 #include "ui/views/widget/tooltip_manager.h"
89 #include "ui/views/widget/widget.h"
90 #include "ui/views/window/non_client_view.h"
92 using base::UserMetricsAction;
93 using bookmarks::BookmarkModel;
94 using bookmarks::BookmarkNode;
95 using bookmarks::BookmarkNodeData;
96 using content::OpenURLParams;
97 using content::PageNavigator;
98 using content::Referrer;
99 using ui::DropTargetEvent;
100 using views::CustomButton;
101 using views::LabelButtonBorder;
102 using views::MenuButton;
103 using views::View;
105 // How inset the bookmarks bar is when displayed on the new tab page.
106 static const int kNewTabHorizontalPadding = 2;
108 // Maximum size of buttons on the bookmark bar.
109 static const int kMaxButtonWidth = 150;
111 // The color gradient start value close to the edge of the divider.
112 static const SkColor kEdgeDividerColor = SkColorSetRGB(222, 234, 248);
114 // The color gradient value for the middle of the divider.
115 static const SkColor kMiddleDividerColor = SkColorSetRGB(194, 205, 212);
117 // Number of pixels the attached bookmark bar overlaps with the toolbar.
118 static const int kToolbarAttachedBookmarkBarOverlap = 3;
120 // Margins around the content.
121 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the
122 // toolbar above serve as the margin.
123 static const int kBottomMargin = 2;
124 static const int kLeftMargin = 1;
125 static const int kRightMargin = 1;
127 // Padding between buttons.
128 static const int kButtonPadding = 0;
130 // Color of the drop indicator.
131 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
133 // Width of the drop indicator.
134 static const int kDropIndicatorWidth = 2;
136 // Distance between the bottom of the bar and the separator.
137 static const int kSeparatorMargin = 1;
139 // Width of the separator between the recently bookmarked button and the
140 // overflow indicator.
141 static const int kSeparatorWidth = 4;
143 // Starting x-coordinate of the separator line within a separator.
144 static const int kSeparatorStartX = 2;
146 // Left-padding for the instructional text.
147 static const int kInstructionsPadding = 6;
149 // Tag for the 'Other bookmarks' button.
150 static const int kOtherFolderButtonTag = 1;
152 // Tag for the 'Apps Shortcut' button.
153 static const int kAppsShortcutButtonTag = 2;
155 // Preferred padding between text and edge.
156 static const int kButtonPaddingHorizontal = 6;
157 static const int kButtonPaddingVertical = 4;
159 // Tag for the 'Managed bookmarks' button.
160 static const int kManagedFolderButtonTag = 3;
161 // Tag for the 'Supervised bookmarks' button.
162 static const int kSupervisedFolderButtonTag = 4;
164 #if !defined(OS_WIN)
165 static const gfx::ElideBehavior kElideBehavior = gfx::FADE_TAIL;
166 #else
167 // Windows fade eliding causes text to darken; see http://crbug.com/388084
168 static const gfx::ElideBehavior kElideBehavior = gfx::ELIDE_TAIL;
169 #endif
171 namespace {
173 // To enable/disable BookmarkBar animations during testing. In production
174 // animations are enabled by default.
175 bool animations_enabled = true;
177 gfx::ImageSkia* GetImageSkiaNamed(int id) {
178 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id);
181 // BookmarkButtonBase -----------------------------------------------
183 // Base class for buttons used on the bookmark bar.
185 class BookmarkButtonBase : public views::LabelButton {
186 public:
187 BookmarkButtonBase(views::ButtonListener* listener,
188 const base::string16& title)
189 : LabelButton(listener, title) {
190 SetElideBehavior(kElideBehavior);
191 show_animation_.reset(new gfx::SlideAnimation(this));
192 if (!animations_enabled) {
193 // For some reason during testing the events generated by animating
194 // throw off the test. So, don't animate while testing.
195 show_animation_->Reset(1);
196 } else {
197 show_animation_->Show();
201 View* GetTooltipHandlerForPoint(const gfx::Point& point) override {
202 return HitTestPoint(point) && CanProcessEventsWithinSubtree() ? this : NULL;
205 scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const override {
206 scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder();
207 border->set_insets(gfx::Insets(kButtonPaddingVertical,
208 kButtonPaddingHorizontal,
209 kButtonPaddingVertical,
210 kButtonPaddingHorizontal));
211 return border.Pass();
214 bool IsTriggerableEvent(const ui::Event& e) override {
215 return e.type() == ui::ET_GESTURE_TAP ||
216 e.type() == ui::ET_GESTURE_TAP_DOWN ||
217 event_utils::IsPossibleDispositionEvent(e);
220 private:
221 scoped_ptr<gfx::SlideAnimation> show_animation_;
223 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase);
226 // BookmarkButton -------------------------------------------------------------
228 // Buttons used for the bookmarks on the bookmark bar.
230 class BookmarkButton : public BookmarkButtonBase {
231 public:
232 // The internal view class name.
233 static const char kViewClassName[];
235 BookmarkButton(views::ButtonListener* listener,
236 const GURL& url,
237 const base::string16& title,
238 Profile* profile)
239 : BookmarkButtonBase(listener, title),
240 url_(url),
241 profile_(profile) {
244 bool GetTooltipText(const gfx::Point& p,
245 base::string16* tooltip) const override {
246 gfx::Point location(p);
247 ConvertPointToScreen(this, &location);
248 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle(
249 GetWidget(), location, url_, GetText(), profile_);
250 return !tooltip->empty();
253 const char* GetClassName() const override { return kViewClassName; }
255 private:
256 const GURL& url_;
257 Profile* profile_;
259 DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
262 // static
263 const char BookmarkButton::kViewClassName[] = "BookmarkButton";
265 // ShortcutButton -------------------------------------------------------------
267 // Buttons used for the shortcuts on the bookmark bar.
269 class ShortcutButton : public BookmarkButtonBase {
270 public:
271 // The internal view class name.
272 static const char kViewClassName[];
274 ShortcutButton(views::ButtonListener* listener,
275 const base::string16& title)
276 : BookmarkButtonBase(listener, title) {
279 const char* GetClassName() const override { return kViewClassName; }
281 private:
282 DISALLOW_COPY_AND_ASSIGN(ShortcutButton);
285 // static
286 const char ShortcutButton::kViewClassName[] = "ShortcutButton";
288 // BookmarkFolderButton -------------------------------------------------------
290 // Buttons used for folders on the bookmark bar, including the 'other folders'
291 // button.
292 class BookmarkFolderButton : public views::MenuButton {
293 public:
294 BookmarkFolderButton(views::ButtonListener* listener,
295 const base::string16& title,
296 views::MenuButtonListener* menu_button_listener,
297 bool show_menu_marker)
298 : MenuButton(listener, title, menu_button_listener, show_menu_marker) {
299 SetElideBehavior(kElideBehavior);
300 show_animation_.reset(new gfx::SlideAnimation(this));
301 if (!animations_enabled) {
302 // For some reason during testing the events generated by animating
303 // throw off the test. So, don't animate while testing.
304 show_animation_->Reset(1);
305 } else {
306 show_animation_->Show();
310 bool GetTooltipText(const gfx::Point& p,
311 base::string16* tooltip) const override {
312 if (label()->GetPreferredSize().width() > label()->size().width())
313 *tooltip = GetText();
314 return !tooltip->empty();
317 bool IsTriggerableEvent(const ui::Event& e) override {
318 // Left clicks and taps should show the menu contents and right clicks
319 // should show the context menu. They should not trigger the opening of
320 // underlying urls.
321 if (e.type() == ui::ET_GESTURE_TAP ||
322 (e.IsMouseEvent() && (e.flags() &
323 (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON))))
324 return false;
326 if (e.IsMouseEvent())
327 return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB;
328 return false;
331 private:
332 scoped_ptr<gfx::SlideAnimation> show_animation_;
334 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
337 // OverFlowButton (chevron) --------------------------------------------------
339 class OverFlowButton : public views::MenuButton {
340 public:
341 explicit OverFlowButton(BookmarkBarView* owner)
342 : MenuButton(NULL, base::string16(), owner, false),
343 owner_(owner) {}
345 bool OnMousePressed(const ui::MouseEvent& e) override {
346 owner_->StopThrobbing(true);
347 return views::MenuButton::OnMousePressed(e);
350 private:
351 BookmarkBarView* owner_;
353 DISALLOW_COPY_AND_ASSIGN(OverFlowButton);
356 void RecordAppLaunch(Profile* profile, const GURL& url) {
357 const extensions::Extension* extension =
358 extensions::ExtensionRegistry::Get(profile)
359 ->enabled_extensions().GetAppByURL(url);
360 if (!extension)
361 return;
363 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR,
364 extension->GetType());
367 } // namespace
369 // DropLocation ---------------------------------------------------------------
371 struct BookmarkBarView::DropLocation {
372 DropLocation()
373 : index(-1),
374 operation(ui::DragDropTypes::DRAG_NONE),
375 on(false),
376 button_type(DROP_BOOKMARK) {
379 bool Equals(const DropLocation& other) {
380 return ((other.index == index) && (other.on == on) &&
381 (other.button_type == button_type));
384 // Index into the model the drop is over. This is relative to the root node.
385 int index;
387 // Drop constants.
388 int operation;
390 // If true, the user is dropping on a folder.
391 bool on;
393 // Type of button.
394 DropButtonType button_type;
397 // DropInfo -------------------------------------------------------------------
399 // Tracks drops on the BookmarkBarView.
401 struct BookmarkBarView::DropInfo {
402 DropInfo()
403 : valid(false),
404 is_menu_showing(false),
405 x(0),
406 y(0) {
409 // Whether the data is valid.
410 bool valid;
412 // If true, the menu is being shown.
413 bool is_menu_showing;
415 // Coordinates of the drag (in terms of the BookmarkBarView).
416 int x;
417 int y;
419 // DropData for the drop.
420 BookmarkNodeData data;
422 DropLocation location;
425 // ButtonSeparatorView --------------------------------------------------------
427 // Paints a themed gradient divider at location |x|. |height| is the full
428 // height of the view you want to paint the divider into, not the height of
429 // the divider. The height of the divider will become:
430 // |height| - 2 * |vertical_padding|.
431 // The color of the divider is a gradient starting with |top_color| at the
432 // top, and changing into |middle_color| and then over to |bottom_color| as
433 // you go further down.
434 void PaintVerticalDivider(gfx::Canvas* canvas,
435 int x,
436 int height,
437 int vertical_padding,
438 SkColor top_color,
439 SkColor middle_color,
440 SkColor bottom_color) {
441 // Draw the upper half of the divider.
442 SkPaint paint;
443 skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
444 vertical_padding + 1, height / 2, top_color, middle_color);
445 paint.setShader(shader.get());
446 SkRect rc = { SkIntToScalar(x),
447 SkIntToScalar(vertical_padding + 1),
448 SkIntToScalar(x + 1),
449 SkIntToScalar(height / 2) };
450 canvas->sk_canvas()->drawRect(rc, paint);
452 // Draw the lower half of the divider.
453 SkPaint paint_down;
454 shader = gfx::CreateGradientShader(
455 height / 2, height - vertical_padding, middle_color, bottom_color);
456 paint_down.setShader(shader.get());
457 SkRect rc_down = { SkIntToScalar(x),
458 SkIntToScalar(height / 2),
459 SkIntToScalar(x + 1),
460 SkIntToScalar(height - vertical_padding) };
461 canvas->sk_canvas()->drawRect(rc_down, paint_down);
464 class BookmarkBarView::ButtonSeparatorView : public views::View {
465 public:
466 ButtonSeparatorView() {}
467 ~ButtonSeparatorView() override {}
469 void OnPaint(gfx::Canvas* canvas) override {
470 PaintVerticalDivider(
471 canvas,
472 kSeparatorStartX,
473 height(),
475 kEdgeDividerColor,
476 kMiddleDividerColor,
477 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR));
480 gfx::Size GetPreferredSize() const override {
481 // We get the full height of the bookmark bar, so that the height returned
482 // here doesn't matter.
483 return gfx::Size(kSeparatorWidth, 1);
486 void GetAccessibleState(ui::AXViewState* state) override {
487 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR);
488 state->role = ui::AX_ROLE_SPLITTER;
491 private:
492 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
495 // BookmarkBarView ------------------------------------------------------------
497 // static
498 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView";
500 BookmarkBarView::BookmarkBarView(Browser* browser, BrowserView* browser_view)
501 : page_navigator_(NULL),
502 client_(NULL),
503 bookmark_menu_(NULL),
504 bookmark_drop_menu_(NULL),
505 other_bookmarks_button_(NULL),
506 managed_bookmarks_button_(NULL),
507 supervised_bookmarks_button_(NULL),
508 apps_page_shortcut_(NULL),
509 overflow_button_(NULL),
510 instructions_(NULL),
511 bookmarks_separator_view_(NULL),
512 browser_(browser),
513 browser_view_(browser_view),
514 infobar_visible_(false),
515 throbbing_view_(NULL),
516 bookmark_bar_state_(BookmarkBar::SHOW),
517 animating_detached_(false),
518 show_folder_method_factory_(this) {
519 set_id(VIEW_ID_BOOKMARK_BAR);
520 Init();
522 size_animation_->Reset(1);
525 BookmarkBarView::~BookmarkBarView() {
526 if (model_)
527 model_->RemoveObserver(this);
529 // It's possible for the menu to outlive us, reset the observer to make sure
530 // it doesn't have a reference to us.
531 if (bookmark_menu_) {
532 bookmark_menu_->set_observer(NULL);
533 bookmark_menu_->SetPageNavigator(NULL);
534 bookmark_menu_->clear_bookmark_bar();
536 if (context_menu_.get())
537 context_menu_->SetPageNavigator(NULL);
539 StopShowFolderDropMenuTimer();
542 // static
543 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) {
544 animations_enabled = !disabled;
547 void BookmarkBarView::AddObserver(BookmarkBarViewObserver* observer) {
548 observers_.AddObserver(observer);
551 void BookmarkBarView::RemoveObserver(BookmarkBarViewObserver* observer) {
552 observers_.RemoveObserver(observer);
555 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
556 page_navigator_ = navigator;
557 if (bookmark_menu_)
558 bookmark_menu_->SetPageNavigator(navigator);
559 if (context_menu_.get())
560 context_menu_->SetPageNavigator(navigator);
563 void BookmarkBarView::SetBookmarkBarState(
564 BookmarkBar::State state,
565 BookmarkBar::AnimateChangeType animate_type) {
566 if (animate_type == BookmarkBar::ANIMATE_STATE_CHANGE &&
567 animations_enabled) {
568 animating_detached_ = (state == BookmarkBar::DETACHED ||
569 bookmark_bar_state_ == BookmarkBar::DETACHED);
570 if (state == BookmarkBar::SHOW)
571 size_animation_->Show();
572 else
573 size_animation_->Hide();
574 } else {
575 size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0);
577 bookmark_bar_state_ = state;
580 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const {
581 if (!infobar_visible_ && browser_->window()->IsFullscreen()) {
582 // There is no client edge to overlap when detached in fullscreen with no
583 // infobars visible.
584 return 0;
586 return views::NonClientFrameView::kClientEdgeThickness;
589 bool BookmarkBarView::is_animating() {
590 return size_animation_->is_animating();
593 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
594 const gfx::Point& loc,
595 int* model_start_index) {
596 *model_start_index = 0;
598 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
599 return NULL;
601 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
603 // Check the managed button first.
604 if (managed_bookmarks_button_->visible() &&
605 managed_bookmarks_button_->bounds().Contains(adjusted_loc)) {
606 return client_->managed_node();
609 // Then check the supervised button.
610 if (supervised_bookmarks_button_->visible() &&
611 supervised_bookmarks_button_->bounds().Contains(adjusted_loc)) {
612 return client_->supervised_node();
615 // Then check the bookmark buttons.
616 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
617 views::View* child = child_at(i);
618 if (!child->visible())
619 break;
620 if (child->bounds().Contains(adjusted_loc))
621 return model_->bookmark_bar_node()->GetChild(i);
624 // Then the overflow button.
625 if (overflow_button_->visible() &&
626 overflow_button_->bounds().Contains(adjusted_loc)) {
627 *model_start_index = GetFirstHiddenNodeIndex();
628 return model_->bookmark_bar_node();
631 // And finally the other folder.
632 if (other_bookmarks_button_->visible() &&
633 other_bookmarks_button_->bounds().Contains(adjusted_loc)) {
634 return model_->other_node();
637 return NULL;
640 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
641 const BookmarkNode* node) {
642 if (node == client_->managed_node())
643 return managed_bookmarks_button_;
644 if (node == client_->supervised_node())
645 return supervised_bookmarks_button_;
646 if (node == model_->other_node())
647 return other_bookmarks_button_;
648 if (node == model_->bookmark_bar_node())
649 return overflow_button_;
650 int index = model_->bookmark_bar_node()->GetIndexOf(node);
651 if (index == -1 || !node->is_folder())
652 return NULL;
653 return static_cast<views::MenuButton*>(child_at(index));
656 void BookmarkBarView::GetAnchorPositionForButton(
657 views::MenuButton* button,
658 views::MenuAnchorPosition* anchor) {
659 if (button == other_bookmarks_button_ || button == overflow_button_)
660 *anchor = views::MENU_ANCHOR_TOPRIGHT;
661 else
662 *anchor = views::MENU_ANCHOR_TOPLEFT;
665 views::MenuItemView* BookmarkBarView::GetMenu() {
666 return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
669 views::MenuItemView* BookmarkBarView::GetContextMenu() {
670 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
673 views::MenuItemView* BookmarkBarView::GetDropMenu() {
674 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
677 void BookmarkBarView::StopThrobbing(bool immediate) {
678 if (!throbbing_view_)
679 return;
681 // If not immediate, cycle through 2 more complete cycles.
682 throbbing_view_->StartThrobbing(immediate ? 0 : 4);
683 throbbing_view_ = NULL;
686 // static
687 base::string16 BookmarkBarView::CreateToolTipForURLAndTitle(
688 const views::Widget* widget,
689 const gfx::Point& screen_loc,
690 const GURL& url,
691 const base::string16& title,
692 Profile* profile) {
693 const views::TooltipManager* tooltip_manager = widget->GetTooltipManager();
694 int max_width = tooltip_manager->GetMaxWidth(screen_loc,
695 widget->GetNativeView());
696 const gfx::FontList tt_fonts = tooltip_manager->GetFontList();
697 base::string16 result;
699 // First the title.
700 if (!title.empty()) {
701 base::string16 localized_title = title;
702 base::i18n::AdjustStringForLocaleDirection(&localized_title);
703 result.append(gfx::ElideText(localized_title, tt_fonts, max_width,
704 gfx::ELIDE_TAIL));
707 // Only show the URL if the url and title differ.
708 if (title != base::UTF8ToUTF16(url.spec())) {
709 if (!result.empty())
710 result.push_back('\n');
712 // We need to explicitly specify the directionality of the URL's text to
713 // make sure it is treated as an LTR string when the context is RTL. For
714 // example, the URL "http://www.yahoo.com/" appears as
715 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
716 // the Unicode BiDi algorithm puts certain characters on the left by
717 // default.
718 std::string languages = profile->GetPrefs()->GetString(
719 prefs::kAcceptLanguages);
720 base::string16 elided_url(ElideUrl(url, tt_fonts, max_width, languages));
721 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
722 result.append(elided_url);
724 return result;
727 bool BookmarkBarView::IsDetached() const {
728 return (bookmark_bar_state_ == BookmarkBar::DETACHED) ||
729 (animating_detached_ && size_animation_->is_animating());
732 double BookmarkBarView::GetAnimationValue() const {
733 return size_animation_->GetCurrentValue();
736 int BookmarkBarView::GetToolbarOverlap() const {
737 int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
738 views::NonClientFrameView::kClientEdgeThickness;
739 if (!IsDetached())
740 return attached_overlap;
742 int detached_overlap = GetFullyDetachedToolbarOverlap();
744 // Do not animate the overlap when the infobar is above us (i.e. when we're
745 // detached), since drawing over the infobar looks weird.
746 if (infobar_visible_)
747 return detached_overlap;
749 // When detached with no infobar, animate the overlap between the attached and
750 // detached states.
751 return detached_overlap + static_cast<int>(
752 (attached_overlap - detached_overlap) *
753 size_animation_->GetCurrentValue());
756 gfx::Size BookmarkBarView::GetPreferredSize() const {
757 gfx::Size prefsize;
758 if (IsDetached()) {
759 prefsize.set_height(
760 chrome::kBookmarkBarHeight +
761 static_cast<int>(
762 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
763 (1 - size_animation_->GetCurrentValue())));
764 } else {
765 prefsize.set_height(static_cast<int>(chrome::kBookmarkBarHeight *
766 size_animation_->GetCurrentValue()));
768 return prefsize;
771 bool BookmarkBarView::CanProcessEventsWithinSubtree() const {
772 // If the bookmark bar is attached and the omnibox popup is open (on top of
773 // the bar), prevent events from targeting the bookmark bar or any of its
774 // descendants. This will prevent hovers/clicks just above the omnibox popup
775 // from activating the top few pixels of items on the bookmark bar.
776 if (!IsDetached() && browser_view_ &&
777 browser_view_->GetLocationBar()->GetOmniboxView()->model()->
778 popup_model()->IsOpen()) {
779 return false;
781 return true;
784 gfx::Size BookmarkBarView::GetMinimumSize() const {
785 // The minimum width of the bookmark bar should at least contain the overflow
786 // button, by which one can access all the Bookmark Bar items, and the "Other
787 // Bookmarks" folder, along with appropriate margins and button padding.
788 // It should also contain the Managed and/or Supervised Bookmarks folders,
789 // if they are visible.
790 int width = kLeftMargin;
792 int height = chrome::kBookmarkBarHeight;
793 if (IsDetached()) {
794 double current_state = 1 - size_animation_->GetCurrentValue();
795 width += 2 * static_cast<int>(kNewTabHorizontalPadding * current_state);
796 height += static_cast<int>(
797 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
798 current_state);
801 if (managed_bookmarks_button_->visible()) {
802 gfx::Size size = managed_bookmarks_button_->GetPreferredSize();
803 width += size.width() + kButtonPadding;
805 if (supervised_bookmarks_button_->visible()) {
806 gfx::Size size = supervised_bookmarks_button_->GetPreferredSize();
807 width += size.width() + kButtonPadding;
809 if (other_bookmarks_button_->visible()) {
810 gfx::Size size = other_bookmarks_button_->GetPreferredSize();
811 width += size.width() + kButtonPadding;
813 if (overflow_button_->visible()) {
814 gfx::Size size = overflow_button_->GetPreferredSize();
815 width += size.width() + kButtonPadding;
817 if (bookmarks_separator_view_->visible()) {
818 gfx::Size size = bookmarks_separator_view_->GetPreferredSize();
819 width += size.width();
821 if (apps_page_shortcut_->visible()) {
822 gfx::Size size = apps_page_shortcut_->GetPreferredSize();
823 width += size.width() + kButtonPadding;
826 return gfx::Size(width, height);
829 void BookmarkBarView::Layout() {
830 // Skip layout during destruction, when no model exists.
831 if (!model_)
832 return;
834 int x = kLeftMargin;
835 int top_margin = IsDetached() ? kDetachedTopMargin : 0;
836 int y = top_margin;
837 int width = View::width() - kRightMargin - kLeftMargin;
838 int height = chrome::kBookmarkBarHeight - kBottomMargin;
839 int separator_margin = kSeparatorMargin;
841 if (IsDetached()) {
842 double current_state = 1 - size_animation_->GetCurrentValue();
843 x += static_cast<int>(kNewTabHorizontalPadding * current_state);
844 y += (View::height() - chrome::kBookmarkBarHeight) / 2;
845 width -= static_cast<int>(kNewTabHorizontalPadding * current_state);
846 separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
847 } else {
848 // For the attached appearance, pin the content to the bottom of the bar
849 // when animating in/out, as shrinking its height instead looks weird. This
850 // also matches how we layout infobars.
851 y += View::height() - chrome::kBookmarkBarHeight;
854 gfx::Size other_bookmarks_pref = other_bookmarks_button_->visible() ?
855 other_bookmarks_button_->GetPreferredSize() : gfx::Size();
856 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
857 gfx::Size bookmarks_separator_pref =
858 bookmarks_separator_view_->GetPreferredSize();
859 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ?
860 apps_page_shortcut_->GetPreferredSize() : gfx::Size();
862 int max_x = width - overflow_pref.width() - kButtonPadding -
863 bookmarks_separator_pref.width();
864 if (other_bookmarks_button_->visible())
865 max_x -= other_bookmarks_pref.width() + kButtonPadding;
867 // Start with the apps page shortcut button.
868 if (apps_page_shortcut_->visible()) {
869 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(),
870 height);
871 x += apps_page_shortcut_pref.width() + kButtonPadding;
874 // Then comes the managed bookmarks folder, if visible.
875 if (managed_bookmarks_button_->visible()) {
876 gfx::Size managed_bookmarks_pref =
877 managed_bookmarks_button_->GetPreferredSize();
878 managed_bookmarks_button_->SetBounds(x, y, managed_bookmarks_pref.width(),
879 height);
880 x += managed_bookmarks_pref.width() + kButtonPadding;
883 // Then the supervised bookmarks folder, if visible.
884 if (supervised_bookmarks_button_->visible()) {
885 gfx::Size supervised_bookmarks_pref =
886 supervised_bookmarks_button_->GetPreferredSize();
887 supervised_bookmarks_button_->SetBounds(
888 x, y, supervised_bookmarks_pref.width(), height);
889 x += supervised_bookmarks_pref.width() + kButtonPadding;
892 const bool show_instructions =
893 model_ && model_->loaded() &&
894 model_->bookmark_bar_node()->child_count() == 0;
895 instructions_->SetVisible(show_instructions);
896 if (show_instructions) {
897 gfx::Size pref = instructions_->GetPreferredSize();
898 instructions_->SetBounds(
899 x + kInstructionsPadding, y,
900 std::min(static_cast<int>(pref.width()),
901 max_x - x),
902 height);
903 } else {
904 bool last_visible = x < max_x;
905 int button_count = GetBookmarkButtonCount();
906 for (int i = 0; i <= button_count; ++i) {
907 if (i == button_count) {
908 // Add another button if there is room for it (and there is another
909 // button to load).
910 if (!last_visible || !model_->loaded() ||
911 model_->bookmark_bar_node()->child_count() <= button_count)
912 break;
913 AddChildViewAt(
914 CreateBookmarkButton(model_->bookmark_bar_node()->GetChild(i)), i);
915 button_count = GetBookmarkButtonCount();
917 views::View* child = child_at(i);
918 gfx::Size pref = child->GetPreferredSize();
919 int next_x = x + pref.width() + kButtonPadding;
920 last_visible = next_x < max_x;
921 child->SetVisible(last_visible);
922 // Only need to set bounds if the view is actually visible.
923 if (last_visible)
924 child->SetBounds(x, y, pref.width(), height);
925 x = next_x;
929 // Layout the right side buttons.
930 x = max_x + kButtonPadding;
932 // The overflow button.
933 overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
934 const bool show_overflow =
935 model_->loaded() &&
936 (model_->bookmark_bar_node()->child_count() > GetBookmarkButtonCount() ||
937 (GetBookmarkButtonCount() > 0 &&
938 !GetBookmarkButton(GetBookmarkButtonCount() - 1)->visible()));
939 overflow_button_->SetVisible(show_overflow);
940 x += overflow_pref.width();
942 // Separator.
943 if (bookmarks_separator_view_->visible()) {
944 bookmarks_separator_view_->SetBounds(x,
945 y - top_margin,
946 bookmarks_separator_pref.width(),
947 height + top_margin + kBottomMargin -
948 separator_margin);
950 x += bookmarks_separator_pref.width();
953 // The "Other Bookmarks" button.
954 if (other_bookmarks_button_->visible()) {
955 other_bookmarks_button_->SetBounds(x, y, other_bookmarks_pref.width(),
956 height);
957 x += other_bookmarks_pref.width() + kButtonPadding;
961 void BookmarkBarView::ViewHierarchyChanged(
962 const ViewHierarchyChangedDetails& details) {
963 if (details.is_add && details.child == this) {
964 // We may get inserted into a hierarchy with a profile - this typically
965 // occurs when the bar's contents get populated fast enough that the
966 // buttons are created before the bar is attached to a frame.
967 UpdateColors();
969 if (height() > 0) {
970 // We only layout while parented. When we become parented, if our bounds
971 // haven't changed, OnBoundsChanged() won't get invoked and we won't
972 // layout. Therefore we always force a layout when added.
973 Layout();
978 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas,
979 const views::CullSet& cull_set) {
980 View::PaintChildren(canvas, cull_set);
982 if (drop_info_.get() && drop_info_->valid &&
983 drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
984 drop_info_->location.button_type != DROP_OVERFLOW &&
985 !drop_info_->location.on) {
986 int index = drop_info_->location.index;
987 DCHECK(index <= GetBookmarkButtonCount());
988 int x = 0;
989 int y = 0;
990 int h = height();
991 if (index == GetBookmarkButtonCount()) {
992 if (index == 0) {
993 x = kLeftMargin;
994 } else {
995 x = GetBookmarkButton(index - 1)->x() +
996 GetBookmarkButton(index - 1)->width();
998 } else {
999 x = GetBookmarkButton(index)->x();
1001 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
1002 y = GetBookmarkButton(0)->y();
1003 h = GetBookmarkButton(0)->height();
1006 // Since the drop indicator is painted directly onto the canvas, we must
1007 // make sure it is painted in the right location if the locale is RTL.
1008 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
1010 kDropIndicatorWidth,
1012 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
1014 // TODO(sky/glen): make me pretty!
1015 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
1019 bool BookmarkBarView::GetDropFormats(
1020 int* formats,
1021 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
1022 if (!model_ || !model_->loaded())
1023 return false;
1024 *formats = ui::OSExchangeData::URL;
1025 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat());
1026 return true;
1029 bool BookmarkBarView::AreDropTypesRequired() {
1030 return true;
1033 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
1034 if (!model_ || !model_->loaded() ||
1035 !browser_->profile()->GetPrefs()->GetBoolean(
1036 bookmarks::prefs::kEditBookmarksEnabled))
1037 return false;
1039 if (!drop_info_.get())
1040 drop_info_.reset(new DropInfo());
1042 // Only accept drops of 1 node, which is the case for all data dragged from
1043 // bookmark bar and menus.
1044 return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
1047 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
1050 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
1051 if (!drop_info_.get())
1052 return 0;
1054 if (drop_info_->valid &&
1055 (drop_info_->x == event.x() && drop_info_->y == event.y())) {
1056 // The location of the mouse didn't change, return the last operation.
1057 return drop_info_->location.operation;
1060 drop_info_->x = event.x();
1061 drop_info_->y = event.y();
1063 DropLocation location;
1064 CalculateDropLocation(event, drop_info_->data, &location);
1066 if (drop_info_->valid && drop_info_->location.Equals(location)) {
1067 // The position we're going to drop didn't change, return the last drag
1068 // operation we calculated. Copy of the operation in case it changed.
1069 drop_info_->location.operation = location.operation;
1070 return drop_info_->location.operation;
1073 StopShowFolderDropMenuTimer();
1075 // TODO(sky): Optimize paint region.
1076 SchedulePaint();
1078 drop_info_->location = location;
1079 drop_info_->valid = true;
1081 if (drop_info_->is_menu_showing) {
1082 if (bookmark_drop_menu_)
1083 bookmark_drop_menu_->Cancel();
1084 drop_info_->is_menu_showing = false;
1087 if (location.on || location.button_type == DROP_OVERFLOW ||
1088 location.button_type == DROP_OTHER_FOLDER) {
1089 const BookmarkNode* node;
1090 if (location.button_type == DROP_OTHER_FOLDER)
1091 node = model_->other_node();
1092 else if (location.button_type == DROP_OVERFLOW)
1093 node = model_->bookmark_bar_node();
1094 else
1095 node = model_->bookmark_bar_node()->GetChild(location.index);
1096 StartShowFolderDropMenuTimer(node);
1099 return drop_info_->location.operation;
1102 void BookmarkBarView::OnDragExited() {
1103 StopShowFolderDropMenuTimer();
1105 // NOTE: we don't hide the menu on exit as it's possible the user moved the
1106 // mouse over the menu, which triggers an exit on us.
1108 drop_info_->valid = false;
1110 if (drop_info_->location.index != -1) {
1111 // TODO(sky): optimize the paint region.
1112 SchedulePaint();
1114 drop_info_.reset();
1117 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
1118 StopShowFolderDropMenuTimer();
1120 if (bookmark_drop_menu_)
1121 bookmark_drop_menu_->Cancel();
1123 if (!drop_info_.get() || !drop_info_->location.operation)
1124 return ui::DragDropTypes::DRAG_NONE;
1126 const BookmarkNode* root =
1127 (drop_info_->location.button_type == DROP_OTHER_FOLDER) ?
1128 model_->other_node() : model_->bookmark_bar_node();
1129 int index = drop_info_->location.index;
1131 if (index != -1) {
1132 // TODO(sky): optimize the SchedulePaint region.
1133 SchedulePaint();
1135 const BookmarkNode* parent_node;
1136 if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
1137 parent_node = root;
1138 index = parent_node->child_count();
1139 } else if (drop_info_->location.on) {
1140 parent_node = root->GetChild(index);
1141 index = parent_node->child_count();
1142 } else {
1143 parent_node = root;
1145 const BookmarkNodeData data = drop_info_->data;
1146 DCHECK(data.is_valid());
1147 bool copy = drop_info_->location.operation == ui::DragDropTypes::DRAG_COPY;
1148 drop_info_.reset();
1149 return chrome::DropBookmarks(
1150 browser_->profile(), data, parent_node, index, copy);
1153 void BookmarkBarView::OnThemeChanged() {
1154 UpdateColors();
1157 const char* BookmarkBarView::GetClassName() const {
1158 return kViewClassName;
1161 void BookmarkBarView::SetVisible(bool v) {
1162 if (v == visible())
1163 return;
1165 View::SetVisible(v);
1166 FOR_EACH_OBSERVER(BookmarkBarViewObserver, observers_,
1167 OnBookmarkBarVisibilityChanged());
1170 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) {
1171 state->role = ui::AX_ROLE_TOOLBAR;
1172 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
1175 void BookmarkBarView::AnimationProgressed(const gfx::Animation* animation) {
1176 // |browser_view_| can be NULL during tests.
1177 if (browser_view_)
1178 browser_view_->ToolbarSizeChanged(true);
1181 void BookmarkBarView::AnimationEnded(const gfx::Animation* animation) {
1182 // |browser_view_| can be NULL during tests.
1183 if (browser_view_) {
1184 browser_view_->ToolbarSizeChanged(false);
1185 SchedulePaint();
1189 void BookmarkBarView::BookmarkMenuControllerDeleted(
1190 BookmarkMenuController* controller) {
1191 if (controller == bookmark_menu_)
1192 bookmark_menu_ = NULL;
1193 else if (controller == bookmark_drop_menu_)
1194 bookmark_drop_menu_ = NULL;
1197 void BookmarkBarView::OnImportBookmarks() {
1198 int64 install_time = g_browser_process->metrics_service()->GetInstallDate();
1199 int64 time_from_install = base::Time::Now().ToTimeT() - install_time;
1200 if (bookmark_bar_state_ == BookmarkBar::SHOW) {
1201 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
1202 time_from_install);
1203 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) {
1204 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1205 time_from_install);
1208 chrome::ShowImportDialog(browser_);
1211 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) {
1212 StopThrobbing(true);
1213 const BookmarkNode* node = model_->GetMostRecentlyAddedUserNodeForURL(url);
1214 if (!node)
1215 return; // Generally shouldn't happen.
1216 StartThrobbing(node, false);
1219 void BookmarkBarView::OnBookmarkBubbleHidden() {
1220 StopThrobbing(false);
1223 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model,
1224 bool ids_reassigned) {
1225 // There should be no buttons. If non-zero it means Load was invoked more than
1226 // once, or we didn't properly clear things. Either of which shouldn't happen.
1227 // The actual bookmark buttons are added from Layout().
1228 DCHECK_EQ(0, GetBookmarkButtonCount());
1229 DCHECK(model->other_node());
1230 other_bookmarks_button_->SetAccessibleName(model->other_node()->GetTitle());
1231 other_bookmarks_button_->SetText(model->other_node()->GetTitle());
1232 managed_bookmarks_button_->SetAccessibleName(
1233 client_->managed_node()->GetTitle());
1234 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1235 supervised_bookmarks_button_->SetAccessibleName(
1236 client_->supervised_node()->GetTitle());
1237 supervised_bookmarks_button_->SetText(client_->supervised_node()->GetTitle());
1238 UpdateColors();
1239 UpdateOtherAndManagedButtonsVisibility();
1240 other_bookmarks_button_->SetEnabled(true);
1241 managed_bookmarks_button_->SetEnabled(true);
1242 supervised_bookmarks_button_->SetEnabled(true);
1243 LayoutAndPaint();
1246 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
1247 NOTREACHED();
1248 // Do minimal cleanup, presumably we'll be deleted shortly.
1249 model_->RemoveObserver(this);
1250 model_ = NULL;
1253 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
1254 const BookmarkNode* old_parent,
1255 int old_index,
1256 const BookmarkNode* new_parent,
1257 int new_index) {
1258 bool was_throbbing = throbbing_view_ &&
1259 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
1260 if (was_throbbing)
1261 throbbing_view_->StopThrobbing();
1262 bool needs_layout_and_paint =
1263 BookmarkNodeRemovedImpl(model, old_parent, old_index);
1264 if (BookmarkNodeAddedImpl(model, new_parent, new_index))
1265 needs_layout_and_paint = true;
1266 if (was_throbbing && new_index < GetBookmarkButtonCount())
1267 StartThrobbing(new_parent->GetChild(new_index), false);
1268 if (needs_layout_and_paint)
1269 LayoutAndPaint();
1272 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
1273 const BookmarkNode* parent,
1274 int index) {
1275 if (BookmarkNodeAddedImpl(model, parent, index))
1276 LayoutAndPaint();
1279 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1280 const BookmarkNode* parent,
1281 int old_index,
1282 const BookmarkNode* node,
1283 const std::set<GURL>& removed_urls) {
1284 // Close the menu if the menu is showing for the deleted node.
1285 if (bookmark_menu_ && bookmark_menu_->node() == node)
1286 bookmark_menu_->Cancel();
1287 if (BookmarkNodeRemovedImpl(model, parent, old_index))
1288 LayoutAndPaint();
1291 void BookmarkBarView::BookmarkAllUserNodesRemoved(
1292 BookmarkModel* model,
1293 const std::set<GURL>& removed_urls) {
1294 UpdateOtherAndManagedButtonsVisibility();
1296 StopThrobbing(true);
1298 // Remove the existing buttons.
1299 while (GetBookmarkButtonCount())
1300 delete GetBookmarkButton(0);
1302 LayoutAndPaint();
1305 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1306 const BookmarkNode* node) {
1307 BookmarkNodeChangedImpl(model, node);
1310 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1311 const BookmarkNode* node) {
1312 if (node != model->bookmark_bar_node())
1313 return; // We only care about reordering of the bookmark bar node.
1315 // Remove the existing buttons.
1316 while (GetBookmarkButtonCount())
1317 delete child_at(0);
1319 // Create the new buttons.
1320 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1321 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1323 LayoutAndPaint();
1326 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model,
1327 const BookmarkNode* node) {
1328 BookmarkNodeChangedImpl(model, node);
1331 void BookmarkBarView::WriteDragDataForView(View* sender,
1332 const gfx::Point& press_pt,
1333 ui::OSExchangeData* data) {
1334 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1336 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1337 if (sender == GetBookmarkButton(i)) {
1338 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1339 const gfx::ImageSkia* icon = nullptr;
1340 if (node->is_url()) {
1341 const gfx::Image& image = model_->GetFavicon(node);
1342 icon = image.IsEmpty() ? GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1343 : image.ToImageSkia();
1344 } else {
1345 icon = GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER);
1348 button_drag_utils::SetDragImage(node->url(),
1349 node->GetTitle(),
1350 *icon,
1351 &press_pt,
1352 data,
1353 GetBookmarkButton(i)->GetWidget());
1354 WriteBookmarkDragData(node, data);
1355 return;
1358 NOTREACHED();
1361 int BookmarkBarView::GetDragOperationsForView(View* sender,
1362 const gfx::Point& p) {
1363 if (size_animation_->is_animating() ||
1364 (size_animation_->GetCurrentValue() == 0 &&
1365 bookmark_bar_state_ != BookmarkBar::DETACHED)) {
1366 // Don't let the user drag while animating open or we're closed (and not
1367 // detached, when detached size_animation_ is always 0). This typically is
1368 // only hit if the user does something to inadvertently trigger DnD such as
1369 // pressing the mouse and hitting control-b.
1370 return ui::DragDropTypes::DRAG_NONE;
1373 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1374 if (sender == GetBookmarkButton(i)) {
1375 return chrome::GetBookmarkDragOperation(
1376 browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
1379 NOTREACHED();
1380 return ui::DragDropTypes::DRAG_NONE;
1383 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1384 const gfx::Point& press_pt,
1385 const gfx::Point& p) {
1386 // Check if we have not moved enough horizontally but we have moved downward
1387 // vertically - downward drag.
1388 gfx::Vector2d move_offset = p - press_pt;
1389 gfx::Vector2d horizontal_offset(move_offset.x(), 0);
1390 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
1391 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1392 if (sender == GetBookmarkButton(i)) {
1393 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1394 // If the folder button was dragged, show the menu instead.
1395 if (node && node->is_folder()) {
1396 views::MenuButton* menu_button =
1397 static_cast<views::MenuButton*>(sender);
1398 menu_button->Activate();
1399 return false;
1401 break;
1405 return true;
1408 void BookmarkBarView::OnMenuButtonClicked(views::View* view,
1409 const gfx::Point& point) {
1410 const BookmarkNode* node;
1412 int start_index = 0;
1413 if (view == other_bookmarks_button_) {
1414 node = model_->other_node();
1415 } else if (view == managed_bookmarks_button_) {
1416 node = client_->managed_node();
1417 } else if (view == supervised_bookmarks_button_) {
1418 node = client_->supervised_node();
1419 } else if (view == overflow_button_) {
1420 node = model_->bookmark_bar_node();
1421 start_index = GetFirstHiddenNodeIndex();
1422 } else {
1423 int button_index = GetIndexOf(view);
1424 DCHECK_NE(-1, button_index);
1425 node = model_->bookmark_bar_node()->GetChild(button_index);
1428 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1429 bookmark_menu_ = new BookmarkMenuController(
1430 browser_, page_navigator_, GetWidget(), node, start_index, false);
1431 bookmark_menu_->set_observer(this);
1432 bookmark_menu_->RunMenuAt(this);
1435 void BookmarkBarView::ButtonPressed(views::Button* sender,
1436 const ui::Event& event) {
1437 WindowOpenDisposition disposition_from_event_flags =
1438 ui::DispositionFromEventFlags(event.flags());
1440 if (sender->tag() == kAppsShortcutButtonTag) {
1441 OpenURLParams params(GURL(chrome::kChromeUIAppsURL),
1442 Referrer(),
1443 disposition_from_event_flags,
1444 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1445 false);
1446 page_navigator_->OpenURL(params);
1447 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1448 return;
1451 const BookmarkNode* node;
1452 if (sender->tag() == kOtherFolderButtonTag) {
1453 node = model_->other_node();
1454 } else if (sender->tag() == kManagedFolderButtonTag) {
1455 node = client_->managed_node();
1456 } else if (sender->tag() == kSupervisedFolderButtonTag) {
1457 node = client_->supervised_node();
1458 } else {
1459 int index = GetIndexOf(sender);
1460 DCHECK_NE(-1, index);
1461 node = model_->bookmark_bar_node()->GetChild(index);
1463 DCHECK(page_navigator_);
1465 if (node->is_url()) {
1466 RecordAppLaunch(browser_->profile(), node->url());
1467 OpenURLParams params(
1468 node->url(), Referrer(), disposition_from_event_flags,
1469 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
1470 page_navigator_->OpenURL(params);
1471 } else {
1472 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node,
1473 disposition_from_event_flags, browser_->profile());
1476 RecordBookmarkLaunch(node, GetBookmarkLaunchLocation());
1479 void BookmarkBarView::ShowContextMenuForView(views::View* source,
1480 const gfx::Point& point,
1481 ui::MenuSourceType source_type) {
1482 if (!model_->loaded()) {
1483 // Don't do anything if the model isn't loaded.
1484 return;
1487 const BookmarkNode* parent = NULL;
1488 std::vector<const BookmarkNode*> nodes;
1489 if (source == other_bookmarks_button_) {
1490 parent = model_->other_node();
1491 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1492 // sure the user can't edit/delete the node in this case.
1493 nodes.push_back(parent);
1494 } else if (source == managed_bookmarks_button_) {
1495 parent = client_->managed_node();
1496 nodes.push_back(parent);
1497 } else if (source == supervised_bookmarks_button_) {
1498 parent = client_->supervised_node();
1499 nodes.push_back(parent);
1500 } else if (source != this && source != apps_page_shortcut_) {
1501 // User clicked on one of the bookmark buttons, find which one they
1502 // clicked on, except for the apps page shortcut, which must behave as if
1503 // the user clicked on the bookmark bar background.
1504 int bookmark_button_index = GetIndexOf(source);
1505 DCHECK(bookmark_button_index != -1 &&
1506 bookmark_button_index < GetBookmarkButtonCount());
1507 const BookmarkNode* node =
1508 model_->bookmark_bar_node()->GetChild(bookmark_button_index);
1509 nodes.push_back(node);
1510 parent = node->parent();
1511 } else {
1512 parent = model_->bookmark_bar_node();
1513 nodes.push_back(parent);
1515 bool close_on_remove =
1516 (parent == model_->other_node()) && (parent->child_count() == 1);
1518 context_menu_.reset(new BookmarkContextMenu(
1519 GetWidget(), browser_, browser_->profile(),
1520 browser_->tab_strip_model()->GetActiveWebContents(),
1521 parent, nodes, close_on_remove));
1522 context_menu_->RunMenuAt(point, source_type);
1525 void BookmarkBarView::Init() {
1526 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1527 // return NULL. When we're inserted into a hierarchy, we'll call
1528 // UpdateColors(), which will set the appropriate colors for all the objects
1529 // added in this function.
1531 // Child views are traversed in the order they are added. Make sure the order
1532 // they are added matches the visual order.
1533 overflow_button_ = CreateOverflowButton();
1534 AddChildView(overflow_button_);
1536 other_bookmarks_button_ = CreateOtherBookmarksButton();
1537 // We'll re-enable when the model is loaded.
1538 other_bookmarks_button_->SetEnabled(false);
1539 AddChildView(other_bookmarks_button_);
1541 managed_bookmarks_button_ = CreateManagedBookmarksButton();
1542 // Also re-enabled when the model is loaded.
1543 managed_bookmarks_button_->SetEnabled(false);
1544 AddChildView(managed_bookmarks_button_);
1546 supervised_bookmarks_button_ = CreateSupervisedBookmarksButton();
1547 // Also re-enabled when the model is loaded.
1548 supervised_bookmarks_button_->SetEnabled(false);
1549 AddChildView(supervised_bookmarks_button_);
1551 apps_page_shortcut_ = CreateAppsPageShortcutButton();
1552 AddChildView(apps_page_shortcut_);
1553 profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
1554 profile_pref_registrar_.Add(
1555 bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1556 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged,
1557 base::Unretained(this)));
1558 profile_pref_registrar_.Add(
1559 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
1560 base::Bind(&BookmarkBarView::OnShowManagedBookmarksPrefChanged,
1561 base::Unretained(this)));
1562 apps_page_shortcut_->SetVisible(
1563 chrome::ShouldShowAppsShortcutInBookmarkBar(
1564 browser_->profile(), browser_->host_desktop_type()));
1566 bookmarks_separator_view_ = new ButtonSeparatorView();
1567 AddChildView(bookmarks_separator_view_);
1568 UpdateBookmarksSeparatorVisibility();
1570 instructions_ = new BookmarkBarInstructionsView(this);
1571 AddChildView(instructions_);
1573 set_context_menu_controller(this);
1575 size_animation_.reset(new gfx::SlideAnimation(this));
1577 model_ = BookmarkModelFactory::GetForProfile(browser_->profile());
1578 client_ = ChromeBookmarkClientFactory::GetForProfile(browser_->profile());
1579 if (model_) {
1580 model_->AddObserver(this);
1581 if (model_->loaded())
1582 BookmarkModelLoaded(model_, false);
1583 // else case: we'll receive notification back from the BookmarkModel when
1584 // done loading, then we'll populate the bar.
1588 int BookmarkBarView::GetBookmarkButtonCount() const {
1589 // We contain seven non-bookmark button views: managed bookmarks, supervised
1590 // bookmarks, other bookmarks, bookmarks separator, chevrons (for overflow),
1591 // apps page, and the instruction label.
1592 return child_count() - 7;
1595 views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) {
1596 // CHECK as otherwise we may do the wrong cast.
1597 CHECK(index >= 0 && index < GetBookmarkButtonCount());
1598 return static_cast<views::LabelButton*>(child_at(index));
1601 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
1602 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
1603 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
1606 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1607 const int bb_count = GetBookmarkButtonCount();
1608 for (int i = 0; i < bb_count; ++i) {
1609 if (!GetBookmarkButton(i)->visible())
1610 return i;
1612 return bb_count;
1615 MenuButton* BookmarkBarView::CreateOtherBookmarksButton() {
1616 // Title is set in Loaded.
1617 MenuButton* button =
1618 new BookmarkFolderButton(this, base::string16(), this, false);
1619 button->set_id(VIEW_ID_OTHER_BOOKMARKS);
1620 button->SetImage(views::Button::STATE_NORMAL,
1621 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER));
1622 button->set_context_menu_controller(this);
1623 button->set_tag(kOtherFolderButtonTag);
1624 return button;
1627 MenuButton* BookmarkBarView::CreateManagedBookmarksButton() {
1628 // Title is set in Loaded.
1629 MenuButton* button =
1630 new BookmarkFolderButton(this, base::string16(), this, false);
1631 button->set_id(VIEW_ID_MANAGED_BOOKMARKS);
1632 button->SetImage(views::Button::STATE_NORMAL,
1633 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER_MANAGED));
1634 button->set_context_menu_controller(this);
1635 button->set_tag(kManagedFolderButtonTag);
1636 return button;
1639 MenuButton* BookmarkBarView::CreateSupervisedBookmarksButton() {
1640 // Title is set in Loaded.
1641 MenuButton* button =
1642 new BookmarkFolderButton(this, base::string16(), this, false);
1643 button->set_id(VIEW_ID_SUPERVISED_BOOKMARKS);
1644 button->SetImage(views::Button::STATE_NORMAL,
1645 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER_SUPERVISED));
1646 button->set_context_menu_controller(this);
1647 button->set_tag(kSupervisedFolderButtonTag);
1648 return button;
1651 MenuButton* BookmarkBarView::CreateOverflowButton() {
1652 MenuButton* button = new OverFlowButton(this);
1653 button->SetImage(views::Button::STATE_NORMAL,
1654 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS));
1656 // The overflow button's image contains an arrow and therefore it is a
1657 // direction sensitive image and we need to flip it if the UI layout is
1658 // right-to-left.
1660 // By default, menu buttons are not flipped because they generally contain
1661 // text and flipping the gfx::Canvas object will break text rendering. Since
1662 // the overflow button does not contain text, we can safely flip it.
1663 button->EnableCanvasFlippingForRTLUI(true);
1665 // Make visible as necessary.
1666 button->SetVisible(false);
1667 // Set accessibility name.
1668 button->SetAccessibleName(
1669 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
1670 return button;
1673 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1674 if (node->is_url()) {
1675 BookmarkButton* button = new BookmarkButton(
1676 this, node->url(), node->GetTitle(), browser_->profile());
1677 ConfigureButton(node, button);
1678 return button;
1680 views::MenuButton* button =
1681 new BookmarkFolderButton(this, node->GetTitle(), this, false);
1682 button->SetImage(views::Button::STATE_NORMAL,
1683 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER));
1684 ConfigureButton(node, button);
1685 return button;
1688 views::LabelButton* BookmarkBarView::CreateAppsPageShortcutButton() {
1689 views::LabelButton* button = new ShortcutButton(
1690 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME));
1691 button->SetTooltipText(l10n_util::GetStringUTF16(
1692 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP));
1693 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1694 button->SetImage(views::Button::STATE_NORMAL,
1695 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT));
1696 button->set_context_menu_controller(this);
1697 button->set_tag(kAppsShortcutButtonTag);
1698 return button;
1701 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1702 views::LabelButton* button) {
1703 button->SetText(node->GetTitle());
1704 button->SetAccessibleName(node->GetTitle());
1705 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1706 // We don't always have a theme provider (ui tests, for example).
1707 if (GetThemeProvider()) {
1708 button->SetTextColor(
1709 views::Button::STATE_NORMAL,
1710 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
1713 button->SetMinSize(gfx::Size());
1714 button->set_context_menu_controller(this);
1715 button->set_drag_controller(this);
1716 if (node->is_url()) {
1717 const gfx::Image& favicon = model_->GetFavicon(node);
1718 button->SetImage(views::Button::STATE_NORMAL,
1719 favicon.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1720 : *favicon.ToImageSkia());
1722 button->SetMaxSize(gfx::Size(kMaxButtonWidth, 0));
1725 bool BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
1726 const BookmarkNode* parent,
1727 int index) {
1728 const bool needs_layout_and_paint = UpdateOtherAndManagedButtonsVisibility();
1729 if (parent != model->bookmark_bar_node())
1730 return needs_layout_and_paint;
1731 if (index < GetBookmarkButtonCount()) {
1732 const BookmarkNode* node = parent->GetChild(index);
1733 AddChildViewAt(CreateBookmarkButton(node), index);
1734 return true;
1736 // If the new node was added after the last button we've created we may be
1737 // able to fit it. Assume we can by returning true, which forces a Layout()
1738 // and creation of the button (if it fits).
1739 return index == GetBookmarkButtonCount();
1742 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1743 const BookmarkNode* parent,
1744 int index) {
1745 const bool needs_layout = UpdateOtherAndManagedButtonsVisibility();
1747 StopThrobbing(true);
1748 // No need to start throbbing again as the bookmark bubble can't be up at
1749 // the same time as the user reorders.
1751 if (parent != model->bookmark_bar_node()) {
1752 // Only children of the bookmark_bar_node get buttons.
1753 return needs_layout;
1755 if (index >= GetBookmarkButtonCount())
1756 return needs_layout;
1758 delete child_at(index);
1759 return true;
1762 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1763 const BookmarkNode* node) {
1764 if (node == client_->managed_node()) {
1765 // The managed node may have its title updated.
1766 managed_bookmarks_button_->SetAccessibleName(
1767 client_->managed_node()->GetTitle());
1768 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1769 return;
1771 if (node == client_->supervised_node()) {
1772 // The supervised node may have its title updated.
1773 supervised_bookmarks_button_->SetAccessibleName(
1774 client_->supervised_node()->GetTitle());
1775 supervised_bookmarks_button_->SetText(
1776 client_->supervised_node()->GetTitle());
1777 return;
1780 if (node->parent() != model->bookmark_bar_node()) {
1781 // We only care about nodes on the bookmark bar.
1782 return;
1784 int index = model->bookmark_bar_node()->GetIndexOf(node);
1785 DCHECK_NE(-1, index);
1786 if (index >= GetBookmarkButtonCount())
1787 return; // Buttons are created as needed.
1788 views::LabelButton* button = GetBookmarkButton(index);
1789 const int old_pref_width = button->GetPreferredSize().width();
1790 ConfigureButton(node, button);
1791 if (old_pref_width != button->GetPreferredSize().width())
1792 LayoutAndPaint();
1795 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1796 if (bookmark_drop_menu_) {
1797 if (bookmark_drop_menu_->node() == node) {
1798 // Already showing for the specified node.
1799 return;
1801 bookmark_drop_menu_->Cancel();
1804 views::MenuButton* menu_button = GetMenuButtonForNode(node);
1805 if (!menu_button)
1806 return;
1808 int start_index = 0;
1809 if (node == model_->bookmark_bar_node())
1810 start_index = GetFirstHiddenNodeIndex();
1812 drop_info_->is_menu_showing = true;
1813 bookmark_drop_menu_ = new BookmarkMenuController(
1814 browser_, page_navigator_, GetWidget(), node, start_index, true);
1815 bookmark_drop_menu_->set_observer(this);
1816 bookmark_drop_menu_->RunMenuAt(this);
1819 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1820 show_folder_method_factory_.InvalidateWeakPtrs();
1823 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1824 if (!animations_enabled) {
1825 // So that tests can run as fast as possible disable the delay during
1826 // testing.
1827 ShowDropFolderForNode(node);
1828 return;
1830 show_folder_method_factory_.InvalidateWeakPtrs();
1831 base::MessageLoop::current()->PostDelayedTask(
1832 FROM_HERE,
1833 base::Bind(&BookmarkBarView::ShowDropFolderForNode,
1834 show_folder_method_factory_.GetWeakPtr(),
1835 node),
1836 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1839 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event,
1840 const BookmarkNodeData& data,
1841 DropLocation* location) {
1842 DCHECK(model_);
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
1852 // locale is RTL.
1853 int mirrored_x = GetMirroredXInView(event.x());
1855 bool found = false;
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;
1863 found = 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 && client_->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);
1873 return;
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) {
1882 found = true;
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;
1890 } else {
1891 location->index = i + 1;
1893 } else if (button_x < button_w / 2) {
1894 location->index = i;
1895 } else {
1896 location->index = i + 1;
1898 break;
1902 if (!found) {
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();
1915 } else {
1916 return;
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();
1923 } else {
1924 return;
1928 if (location->on) {
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;
1939 } else {
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();
1962 if (parent == bbn)
1963 break;
1964 parent_on_bb = parent;
1966 if (parent_on_bb) {
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, client_->managed_node())) {
1975 throbbing_view_ = managed_bookmarks_button_;
1976 } else if (bookmarks::IsDescendantOf(node, client_->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,
1989 int old_index) {
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);
1997 break;
1999 old_node = parent;
2001 if (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, client_->managed_node()))
2009 return managed_bookmarks_button_;
2010 if (bookmarks::IsDescendantOf(parent, client_->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)
2020 return;
2021 SkColor color =
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,
2028 color);
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();
2036 if (update_other) {
2037 other_bookmarks_button_->SetVisible(has_other_children);
2038 UpdateBookmarksSeparatorVisibility();
2041 bool show_managed = !client_->managed_node()->empty() &&
2042 browser_->profile()->GetPrefs()->GetBoolean(
2043 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
2044 bool update_managed = show_managed != managed_bookmarks_button_->visible();
2045 if (update_managed)
2046 managed_bookmarks_button_->SetVisible(show_managed);
2048 bool show_supervised = !client_->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)
2071 return;
2072 apps_page_shortcut_->SetVisible(visible);
2073 UpdateBookmarksSeparatorVisibility();
2074 LayoutAndPaint();
2077 void BookmarkBarView::OnShowManagedBookmarksPrefChanged() {
2078 if (UpdateOtherAndManagedButtonsVisibility())
2079 LayoutAndPaint();