MacViews: Fix for multiple definitions of chrome::DragBookmarks.
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bar_view.cc
blobd8b0f7db408fa0993f4ee0a1aa76b59f31d4cedc
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/metrics/metrics_service.h"
55 #include "content/public/browser/notification_details.h"
56 #include "content/public/browser/notification_source.h"
57 #include "content/public/browser/page_navigator.h"
58 #include "content/public/browser/render_view_host.h"
59 #include "content/public/browser/render_widget_host_view.h"
60 #include "content/public/browser/user_metrics.h"
61 #include "content/public/browser/web_contents.h"
62 #include "extensions/browser/extension_registry.h"
63 #include "extensions/common/extension.h"
64 #include "extensions/common/extension_set.h"
65 #include "grit/theme_resources.h"
66 #include "ui/accessibility/ax_view_state.h"
67 #include "ui/base/dragdrop/drag_utils.h"
68 #include "ui/base/dragdrop/os_exchange_data.h"
69 #include "ui/base/l10n/l10n_util.h"
70 #include "ui/base/page_transition_types.h"
71 #include "ui/base/resource/resource_bundle.h"
72 #include "ui/base/theme_provider.h"
73 #include "ui/base/window_open_disposition.h"
74 #include "ui/gfx/animation/slide_animation.h"
75 #include "ui/gfx/canvas.h"
76 #include "ui/gfx/text_constants.h"
77 #include "ui/gfx/text_elider.h"
78 #include "ui/resources/grit/ui_resources.h"
79 #include "ui/views/button_drag_utils.h"
80 #include "ui/views/controls/button/label_button.h"
81 #include "ui/views/controls/button/label_button_border.h"
82 #include "ui/views/controls/button/menu_button.h"
83 #include "ui/views/controls/label.h"
84 #include "ui/views/drag_utils.h"
85 #include "ui/views/metrics.h"
86 #include "ui/views/view_constants.h"
87 #include "ui/views/widget/tooltip_manager.h"
88 #include "ui/views/widget/widget.h"
89 #include "ui/views/window/non_client_view.h"
91 using base::UserMetricsAction;
92 using bookmarks::BookmarkNodeData;
93 using content::OpenURLParams;
94 using content::PageNavigator;
95 using content::Referrer;
96 using ui::DropTargetEvent;
97 using views::CustomButton;
98 using views::LabelButtonBorder;
99 using views::MenuButton;
100 using views::View;
102 // How inset the bookmarks bar is when displayed on the new tab page.
103 static const int kNewTabHorizontalPadding = 2;
105 // Maximum size of buttons on the bookmark bar.
106 static const int kMaxButtonWidth = 150;
108 // The color gradient start value close to the edge of the divider.
109 static const SkColor kEdgeDividerColor = SkColorSetRGB(222, 234, 248);
111 // The color gradient value for the middle of the divider.
112 static const SkColor kMiddleDividerColor = SkColorSetRGB(194, 205, 212);
114 // Number of pixels the attached bookmark bar overlaps with the toolbar.
115 static const int kToolbarAttachedBookmarkBarOverlap = 3;
117 // Margins around the content.
118 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the
119 // toolbar above serve as the margin.
120 static const int kBottomMargin = 2;
121 static const int kLeftMargin = 1;
122 static const int kRightMargin = 1;
124 // Padding between buttons.
125 static const int kButtonPadding = 0;
127 // Color of the drop indicator.
128 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
130 // Width of the drop indicator.
131 static const int kDropIndicatorWidth = 2;
133 // Distance between the bottom of the bar and the separator.
134 static const int kSeparatorMargin = 1;
136 // Width of the separator between the recently bookmarked button and the
137 // overflow indicator.
138 static const int kSeparatorWidth = 4;
140 // Starting x-coordinate of the separator line within a separator.
141 static const int kSeparatorStartX = 2;
143 // Left-padding for the instructional text.
144 static const int kInstructionsPadding = 6;
146 // Tag for the 'Other bookmarks' button.
147 static const int kOtherFolderButtonTag = 1;
149 // Tag for the 'Apps Shortcut' button.
150 static const int kAppsShortcutButtonTag = 2;
152 // Preferred padding between text and edge.
153 static const int kButtonPaddingHorizontal = 6;
154 static const int kButtonPaddingVertical = 4;
156 // Tag for the 'Managed bookmarks' button.
157 static const int kManagedFolderButtonTag = 3;
159 #if !defined(OS_WIN)
160 static const gfx::ElideBehavior kElideBehavior = gfx::FADE_TAIL;
161 #else
162 // Windows fade eliding causes text to darken; see http://crbug.com/388084
163 static const gfx::ElideBehavior kElideBehavior = gfx::ELIDE_TAIL;
164 #endif
166 namespace {
168 // To enable/disable BookmarkBar animations during testing. In production
169 // animations are enabled by default.
170 bool animations_enabled = true;
172 gfx::ImageSkia* GetImageSkiaNamed(int id) {
173 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id);
176 // BookmarkButtonBase -----------------------------------------------
178 // Base class for buttons used on the bookmark bar.
180 class BookmarkButtonBase : public views::LabelButton {
181 public:
182 BookmarkButtonBase(views::ButtonListener* listener,
183 const base::string16& title)
184 : LabelButton(listener, title) {
185 SetElideBehavior(kElideBehavior);
186 show_animation_.reset(new gfx::SlideAnimation(this));
187 if (!animations_enabled) {
188 // For some reason during testing the events generated by animating
189 // throw off the test. So, don't animate while testing.
190 show_animation_->Reset(1);
191 } else {
192 show_animation_->Show();
196 View* GetTooltipHandlerForPoint(const gfx::Point& point) override {
197 return HitTestPoint(point) && CanProcessEventsWithinSubtree() ? this : NULL;
200 scoped_ptr<LabelButtonBorder> CreateDefaultBorder() const override {
201 scoped_ptr<LabelButtonBorder> border = LabelButton::CreateDefaultBorder();
202 border->set_insets(gfx::Insets(kButtonPaddingVertical,
203 kButtonPaddingHorizontal,
204 kButtonPaddingVertical,
205 kButtonPaddingHorizontal));
206 return border.Pass();
209 bool IsTriggerableEvent(const ui::Event& e) override {
210 return e.type() == ui::ET_GESTURE_TAP ||
211 e.type() == ui::ET_GESTURE_TAP_DOWN ||
212 event_utils::IsPossibleDispositionEvent(e);
215 private:
216 scoped_ptr<gfx::SlideAnimation> show_animation_;
218 DISALLOW_COPY_AND_ASSIGN(BookmarkButtonBase);
221 // BookmarkButton -------------------------------------------------------------
223 // Buttons used for the bookmarks on the bookmark bar.
225 class BookmarkButton : public BookmarkButtonBase {
226 public:
227 // The internal view class name.
228 static const char kViewClassName[];
230 BookmarkButton(views::ButtonListener* listener,
231 const GURL& url,
232 const base::string16& title,
233 Profile* profile)
234 : BookmarkButtonBase(listener, title),
235 url_(url),
236 profile_(profile) {
239 bool GetTooltipText(const gfx::Point& p,
240 base::string16* tooltip) const override {
241 gfx::Point location(p);
242 ConvertPointToScreen(this, &location);
243 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle(
244 GetWidget(), location, url_, GetText(), profile_);
245 return !tooltip->empty();
248 const char* GetClassName() const override { return kViewClassName; }
250 private:
251 const GURL& url_;
252 Profile* profile_;
254 DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
257 // static
258 const char BookmarkButton::kViewClassName[] = "BookmarkButton";
260 // ShortcutButton -------------------------------------------------------------
262 // Buttons used for the shortcuts on the bookmark bar.
264 class ShortcutButton : public BookmarkButtonBase {
265 public:
266 // The internal view class name.
267 static const char kViewClassName[];
269 ShortcutButton(views::ButtonListener* listener,
270 const base::string16& title)
271 : BookmarkButtonBase(listener, title) {
274 const char* GetClassName() const override { return kViewClassName; }
276 private:
277 DISALLOW_COPY_AND_ASSIGN(ShortcutButton);
280 // static
281 const char ShortcutButton::kViewClassName[] = "ShortcutButton";
283 // BookmarkFolderButton -------------------------------------------------------
285 // Buttons used for folders on the bookmark bar, including the 'other folders'
286 // button.
287 class BookmarkFolderButton : public views::MenuButton {
288 public:
289 BookmarkFolderButton(views::ButtonListener* listener,
290 const base::string16& title,
291 views::MenuButtonListener* menu_button_listener,
292 bool show_menu_marker)
293 : MenuButton(listener, title, menu_button_listener, show_menu_marker) {
294 SetElideBehavior(kElideBehavior);
295 show_animation_.reset(new gfx::SlideAnimation(this));
296 if (!animations_enabled) {
297 // For some reason during testing the events generated by animating
298 // throw off the test. So, don't animate while testing.
299 show_animation_->Reset(1);
300 } else {
301 show_animation_->Show();
305 bool GetTooltipText(const gfx::Point& p,
306 base::string16* tooltip) const override {
307 if (label()->GetPreferredSize().width() > label()->size().width())
308 *tooltip = GetText();
309 return !tooltip->empty();
312 bool IsTriggerableEvent(const ui::Event& e) override {
313 // Left clicks and taps should show the menu contents and right clicks
314 // should show the context menu. They should not trigger the opening of
315 // underlying urls.
316 if (e.type() == ui::ET_GESTURE_TAP ||
317 (e.IsMouseEvent() && (e.flags() &
318 (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON))))
319 return false;
321 if (e.IsMouseEvent())
322 return ui::DispositionFromEventFlags(e.flags()) != CURRENT_TAB;
323 return false;
326 private:
327 scoped_ptr<gfx::SlideAnimation> show_animation_;
329 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
332 // OverFlowButton (chevron) --------------------------------------------------
334 class OverFlowButton : public views::MenuButton {
335 public:
336 explicit OverFlowButton(BookmarkBarView* owner)
337 : MenuButton(NULL, base::string16(), owner, false),
338 owner_(owner) {}
340 bool OnMousePressed(const ui::MouseEvent& e) override {
341 owner_->StopThrobbing(true);
342 return views::MenuButton::OnMousePressed(e);
345 private:
346 BookmarkBarView* owner_;
348 DISALLOW_COPY_AND_ASSIGN(OverFlowButton);
351 void RecordAppLaunch(Profile* profile, const GURL& url) {
352 const extensions::Extension* extension =
353 extensions::ExtensionRegistry::Get(profile)
354 ->enabled_extensions().GetAppByURL(url);
355 if (!extension)
356 return;
358 extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_BOOKMARK_BAR,
359 extension->GetType());
362 } // namespace
364 // DropLocation ---------------------------------------------------------------
366 struct BookmarkBarView::DropLocation {
367 DropLocation()
368 : index(-1),
369 operation(ui::DragDropTypes::DRAG_NONE),
370 on(false),
371 button_type(DROP_BOOKMARK) {
374 bool Equals(const DropLocation& other) {
375 return ((other.index == index) && (other.on == on) &&
376 (other.button_type == button_type));
379 // Index into the model the drop is over. This is relative to the root node.
380 int index;
382 // Drop constants.
383 int operation;
385 // If true, the user is dropping on a folder.
386 bool on;
388 // Type of button.
389 DropButtonType button_type;
392 // DropInfo -------------------------------------------------------------------
394 // Tracks drops on the BookmarkBarView.
396 struct BookmarkBarView::DropInfo {
397 DropInfo()
398 : valid(false),
399 is_menu_showing(false),
400 x(0),
401 y(0) {
404 // Whether the data is valid.
405 bool valid;
407 // If true, the menu is being shown.
408 bool is_menu_showing;
410 // Coordinates of the drag (in terms of the BookmarkBarView).
411 int x;
412 int y;
414 // DropData for the drop.
415 BookmarkNodeData data;
417 DropLocation location;
420 // ButtonSeparatorView --------------------------------------------------------
422 // Paints a themed gradient divider at location |x|. |height| is the full
423 // height of the view you want to paint the divider into, not the height of
424 // the divider. The height of the divider will become:
425 // |height| - 2 * |vertical_padding|.
426 // The color of the divider is a gradient starting with |top_color| at the
427 // top, and changing into |middle_color| and then over to |bottom_color| as
428 // you go further down.
429 void PaintVerticalDivider(gfx::Canvas* canvas,
430 int x,
431 int height,
432 int vertical_padding,
433 SkColor top_color,
434 SkColor middle_color,
435 SkColor bottom_color) {
436 // Draw the upper half of the divider.
437 SkPaint paint;
438 skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
439 vertical_padding + 1, height / 2, top_color, middle_color);
440 paint.setShader(shader.get());
441 SkRect rc = { SkIntToScalar(x),
442 SkIntToScalar(vertical_padding + 1),
443 SkIntToScalar(x + 1),
444 SkIntToScalar(height / 2) };
445 canvas->sk_canvas()->drawRect(rc, paint);
447 // Draw the lower half of the divider.
448 SkPaint paint_down;
449 shader = gfx::CreateGradientShader(
450 height / 2, height - vertical_padding, middle_color, bottom_color);
451 paint_down.setShader(shader.get());
452 SkRect rc_down = { SkIntToScalar(x),
453 SkIntToScalar(height / 2),
454 SkIntToScalar(x + 1),
455 SkIntToScalar(height - vertical_padding) };
456 canvas->sk_canvas()->drawRect(rc_down, paint_down);
459 class BookmarkBarView::ButtonSeparatorView : public views::View {
460 public:
461 ButtonSeparatorView() {}
462 ~ButtonSeparatorView() override {}
464 void OnPaint(gfx::Canvas* canvas) override {
465 PaintVerticalDivider(
466 canvas,
467 kSeparatorStartX,
468 height(),
470 kEdgeDividerColor,
471 kMiddleDividerColor,
472 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR));
475 gfx::Size GetPreferredSize() const override {
476 // We get the full height of the bookmark bar, so that the height returned
477 // here doesn't matter.
478 return gfx::Size(kSeparatorWidth, 1);
481 void GetAccessibleState(ui::AXViewState* state) override {
482 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR);
483 state->role = ui::AX_ROLE_SPLITTER;
486 private:
487 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
490 // BookmarkBarView ------------------------------------------------------------
492 // static
493 const char BookmarkBarView::kViewClassName[] = "BookmarkBarView";
495 BookmarkBarView::BookmarkBarView(Browser* browser, BrowserView* browser_view)
496 : page_navigator_(NULL),
497 client_(NULL),
498 bookmark_menu_(NULL),
499 bookmark_drop_menu_(NULL),
500 other_bookmarks_button_(NULL),
501 managed_bookmarks_button_(NULL),
502 apps_page_shortcut_(NULL),
503 overflow_button_(NULL),
504 instructions_(NULL),
505 bookmarks_separator_view_(NULL),
506 browser_(browser),
507 browser_view_(browser_view),
508 infobar_visible_(false),
509 throbbing_view_(NULL),
510 bookmark_bar_state_(BookmarkBar::SHOW),
511 animating_detached_(false),
512 show_folder_method_factory_(this) {
513 set_id(VIEW_ID_BOOKMARK_BAR);
514 Init();
516 size_animation_->Reset(1);
519 BookmarkBarView::~BookmarkBarView() {
520 if (model_)
521 model_->RemoveObserver(this);
523 // It's possible for the menu to outlive us, reset the observer to make sure
524 // it doesn't have a reference to us.
525 if (bookmark_menu_) {
526 bookmark_menu_->set_observer(NULL);
527 bookmark_menu_->SetPageNavigator(NULL);
528 bookmark_menu_->clear_bookmark_bar();
530 if (context_menu_.get())
531 context_menu_->SetPageNavigator(NULL);
533 StopShowFolderDropMenuTimer();
536 // static
537 void BookmarkBarView::DisableAnimationsForTesting(bool disabled) {
538 animations_enabled = !disabled;
541 void BookmarkBarView::AddObserver(BookmarkBarViewObserver* observer) {
542 observers_.AddObserver(observer);
545 void BookmarkBarView::RemoveObserver(BookmarkBarViewObserver* observer) {
546 observers_.RemoveObserver(observer);
549 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
550 page_navigator_ = navigator;
551 if (bookmark_menu_)
552 bookmark_menu_->SetPageNavigator(navigator);
553 if (context_menu_.get())
554 context_menu_->SetPageNavigator(navigator);
557 void BookmarkBarView::SetBookmarkBarState(
558 BookmarkBar::State state,
559 BookmarkBar::AnimateChangeType animate_type) {
560 if (animate_type == BookmarkBar::ANIMATE_STATE_CHANGE &&
561 animations_enabled) {
562 animating_detached_ = (state == BookmarkBar::DETACHED ||
563 bookmark_bar_state_ == BookmarkBar::DETACHED);
564 if (state == BookmarkBar::SHOW)
565 size_animation_->Show();
566 else
567 size_animation_->Hide();
568 } else {
569 size_animation_->Reset(state == BookmarkBar::SHOW ? 1 : 0);
571 bookmark_bar_state_ = state;
574 int BookmarkBarView::GetFullyDetachedToolbarOverlap() const {
575 if (!infobar_visible_ && browser_->window()->IsFullscreen()) {
576 // There is no client edge to overlap when detached in fullscreen with no
577 // infobars visible.
578 return 0;
580 return views::NonClientFrameView::kClientEdgeThickness;
583 bool BookmarkBarView::is_animating() {
584 return size_animation_->is_animating();
587 const BookmarkNode* BookmarkBarView::GetNodeForButtonAtModelIndex(
588 const gfx::Point& loc,
589 int* model_start_index) {
590 *model_start_index = 0;
592 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
593 return NULL;
595 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
597 // Check the managed button first.
598 if (managed_bookmarks_button_->visible() &&
599 managed_bookmarks_button_->bounds().Contains(adjusted_loc)) {
600 return client_->managed_node();
603 // Then check the bookmark buttons.
604 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
605 views::View* child = child_at(i);
606 if (!child->visible())
607 break;
608 if (child->bounds().Contains(adjusted_loc))
609 return model_->bookmark_bar_node()->GetChild(i);
612 // Then the overflow button.
613 if (overflow_button_->visible() &&
614 overflow_button_->bounds().Contains(adjusted_loc)) {
615 *model_start_index = GetFirstHiddenNodeIndex();
616 return model_->bookmark_bar_node();
619 // And finally the other folder.
620 if (other_bookmarks_button_->visible() &&
621 other_bookmarks_button_->bounds().Contains(adjusted_loc)) {
622 return model_->other_node();
625 return NULL;
628 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
629 const BookmarkNode* node) {
630 if (node == client_->managed_node())
631 return managed_bookmarks_button_;
632 if (node == model_->other_node())
633 return other_bookmarks_button_;
634 if (node == model_->bookmark_bar_node())
635 return overflow_button_;
636 int index = model_->bookmark_bar_node()->GetIndexOf(node);
637 if (index == -1 || !node->is_folder())
638 return NULL;
639 return static_cast<views::MenuButton*>(child_at(index));
642 void BookmarkBarView::GetAnchorPositionForButton(
643 views::MenuButton* button,
644 views::MenuAnchorPosition* anchor) {
645 if (button == other_bookmarks_button_ || button == overflow_button_)
646 *anchor = views::MENU_ANCHOR_TOPRIGHT;
647 else
648 *anchor = views::MENU_ANCHOR_TOPLEFT;
651 views::MenuItemView* BookmarkBarView::GetMenu() {
652 return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
655 views::MenuItemView* BookmarkBarView::GetContextMenu() {
656 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
659 views::MenuItemView* BookmarkBarView::GetDropMenu() {
660 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
663 void BookmarkBarView::StopThrobbing(bool immediate) {
664 if (!throbbing_view_)
665 return;
667 // If not immediate, cycle through 2 more complete cycles.
668 throbbing_view_->StartThrobbing(immediate ? 0 : 4);
669 throbbing_view_ = NULL;
672 // static
673 base::string16 BookmarkBarView::CreateToolTipForURLAndTitle(
674 const views::Widget* widget,
675 const gfx::Point& screen_loc,
676 const GURL& url,
677 const base::string16& title,
678 Profile* profile) {
679 int max_width = views::TooltipManager::GetMaxWidth(
680 screen_loc.x(),
681 screen_loc.y(),
682 widget->GetNativeView());
683 const gfx::FontList tt_fonts = widget->GetTooltipManager()->GetFontList();
684 base::string16 result;
686 // First the title.
687 if (!title.empty()) {
688 base::string16 localized_title = title;
689 base::i18n::AdjustStringForLocaleDirection(&localized_title);
690 result.append(gfx::ElideText(localized_title, tt_fonts, max_width,
691 gfx::ELIDE_TAIL));
694 // Only show the URL if the url and title differ.
695 if (title != base::UTF8ToUTF16(url.spec())) {
696 if (!result.empty())
697 result.push_back('\n');
699 // We need to explicitly specify the directionality of the URL's text to
700 // make sure it is treated as an LTR string when the context is RTL. For
701 // example, the URL "http://www.yahoo.com/" appears as
702 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
703 // the Unicode BiDi algorithm puts certain characters on the left by
704 // default.
705 std::string languages = profile->GetPrefs()->GetString(
706 prefs::kAcceptLanguages);
707 base::string16 elided_url(ElideUrl(url, tt_fonts, max_width, languages));
708 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
709 result.append(elided_url);
711 return result;
714 bool BookmarkBarView::IsDetached() const {
715 return (bookmark_bar_state_ == BookmarkBar::DETACHED) ||
716 (animating_detached_ && size_animation_->is_animating());
719 double BookmarkBarView::GetAnimationValue() const {
720 return size_animation_->GetCurrentValue();
723 int BookmarkBarView::GetToolbarOverlap() const {
724 int attached_overlap = kToolbarAttachedBookmarkBarOverlap +
725 views::NonClientFrameView::kClientEdgeThickness;
726 if (!IsDetached())
727 return attached_overlap;
729 int detached_overlap = GetFullyDetachedToolbarOverlap();
731 // Do not animate the overlap when the infobar is above us (i.e. when we're
732 // detached), since drawing over the infobar looks weird.
733 if (infobar_visible_)
734 return detached_overlap;
736 // When detached with no infobar, animate the overlap between the attached and
737 // detached states.
738 return detached_overlap + static_cast<int>(
739 (attached_overlap - detached_overlap) *
740 size_animation_->GetCurrentValue());
743 gfx::Size BookmarkBarView::GetPreferredSize() const {
744 gfx::Size prefsize;
745 if (IsDetached()) {
746 prefsize.set_height(
747 chrome::kBookmarkBarHeight +
748 static_cast<int>(
749 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
750 (1 - size_animation_->GetCurrentValue())));
751 } else {
752 prefsize.set_height(static_cast<int>(chrome::kBookmarkBarHeight *
753 size_animation_->GetCurrentValue()));
755 return prefsize;
758 bool BookmarkBarView::CanProcessEventsWithinSubtree() const {
759 // If the bookmark bar is attached and the omnibox popup is open (on top of
760 // the bar), prevent events from targeting the bookmark bar or any of its
761 // descendants. This will prevent hovers/clicks just above the omnibox popup
762 // from activating the top few pixels of items on the bookmark bar.
763 if (!IsDetached() && browser_view_ &&
764 browser_view_->GetLocationBar()->GetOmniboxView()->model()->
765 popup_model()->IsOpen()) {
766 return false;
768 return true;
771 gfx::Size BookmarkBarView::GetMinimumSize() const {
772 // The minimum width of the bookmark bar should at least contain the overflow
773 // button, by which one can access all the Bookmark Bar items, and the "Other
774 // Bookmarks" folder, along with appropriate margins and button padding.
775 // It should also contain the Managed Bookmarks folder, if it's visible.
776 int width = kLeftMargin;
778 int height = chrome::kBookmarkBarHeight;
779 if (IsDetached()) {
780 double current_state = 1 - size_animation_->GetCurrentValue();
781 width += 2 * static_cast<int>(kNewTabHorizontalPadding * current_state);
782 height += static_cast<int>(
783 (chrome::kNTPBookmarkBarHeight - chrome::kBookmarkBarHeight) *
784 current_state);
787 if (managed_bookmarks_button_->visible()) {
788 gfx::Size size = managed_bookmarks_button_->GetPreferredSize();
789 width += size.width() + kButtonPadding;
791 if (other_bookmarks_button_->visible()) {
792 gfx::Size size = other_bookmarks_button_->GetPreferredSize();
793 width += size.width() + kButtonPadding;
795 if (overflow_button_->visible()) {
796 gfx::Size size = overflow_button_->GetPreferredSize();
797 width += size.width() + kButtonPadding;
799 if (bookmarks_separator_view_->visible()) {
800 gfx::Size size = bookmarks_separator_view_->GetPreferredSize();
801 width += size.width();
803 if (apps_page_shortcut_->visible()) {
804 gfx::Size size = apps_page_shortcut_->GetPreferredSize();
805 width += size.width() + kButtonPadding;
808 return gfx::Size(width, height);
811 void BookmarkBarView::Layout() {
812 // Skip layout during destruction, when no model exists.
813 if (!model_)
814 return;
816 int x = kLeftMargin;
817 int top_margin = IsDetached() ? kDetachedTopMargin : 0;
818 int y = top_margin;
819 int width = View::width() - kRightMargin - kLeftMargin;
820 int height = chrome::kBookmarkBarHeight - kBottomMargin;
821 int separator_margin = kSeparatorMargin;
823 if (IsDetached()) {
824 double current_state = 1 - size_animation_->GetCurrentValue();
825 x += static_cast<int>(kNewTabHorizontalPadding * current_state);
826 y += (View::height() - chrome::kBookmarkBarHeight) / 2;
827 width -= static_cast<int>(kNewTabHorizontalPadding * current_state);
828 separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
829 } else {
830 // For the attached appearance, pin the content to the bottom of the bar
831 // when animating in/out, as shrinking its height instead looks weird. This
832 // also matches how we layout infobars.
833 y += View::height() - chrome::kBookmarkBarHeight;
836 gfx::Size other_bookmarks_pref = other_bookmarks_button_->visible() ?
837 other_bookmarks_button_->GetPreferredSize() : gfx::Size();
838 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
839 gfx::Size bookmarks_separator_pref =
840 bookmarks_separator_view_->GetPreferredSize();
841 gfx::Size apps_page_shortcut_pref = apps_page_shortcut_->visible() ?
842 apps_page_shortcut_->GetPreferredSize() : gfx::Size();
844 int max_x = width - overflow_pref.width() - kButtonPadding -
845 bookmarks_separator_pref.width();
846 if (other_bookmarks_button_->visible())
847 max_x -= other_bookmarks_pref.width() + kButtonPadding;
849 // Start with the apps page shortcut button.
850 if (apps_page_shortcut_->visible()) {
851 apps_page_shortcut_->SetBounds(x, y, apps_page_shortcut_pref.width(),
852 height);
853 x += apps_page_shortcut_pref.width() + kButtonPadding;
856 // Then comes the managed bookmarks folder, if visible.
857 if (managed_bookmarks_button_->visible()) {
858 gfx::Size managed_bookmarks_pref = managed_bookmarks_button_->visible() ?
859 managed_bookmarks_button_->GetPreferredSize() : gfx::Size();
860 managed_bookmarks_button_->SetBounds(x, y, managed_bookmarks_pref.width(),
861 height);
862 x += managed_bookmarks_pref.width() + kButtonPadding;
865 const bool show_instructions =
866 model_ && model_->loaded() &&
867 model_->bookmark_bar_node()->child_count() == 0;
868 instructions_->SetVisible(show_instructions);
869 if (show_instructions) {
870 gfx::Size pref = instructions_->GetPreferredSize();
871 instructions_->SetBounds(
872 x + kInstructionsPadding, y,
873 std::min(static_cast<int>(pref.width()),
874 max_x - x),
875 height);
876 } else {
877 bool last_visible = x < max_x;
878 int button_count = GetBookmarkButtonCount();
879 for (int i = 0; i <= button_count; ++i) {
880 if (i == button_count) {
881 // Add another button if there is room for it (and there is another
882 // button to load).
883 if (!last_visible || !model_->loaded() ||
884 model_->bookmark_bar_node()->child_count() <= button_count)
885 break;
886 AddChildViewAt(
887 CreateBookmarkButton(model_->bookmark_bar_node()->GetChild(i)), i);
888 button_count = GetBookmarkButtonCount();
890 views::View* child = child_at(i);
891 gfx::Size pref = child->GetPreferredSize();
892 int next_x = x + pref.width() + kButtonPadding;
893 last_visible = next_x < max_x;
894 child->SetVisible(last_visible);
895 // Only need to set bounds if the view is actually visible.
896 if (last_visible)
897 child->SetBounds(x, y, pref.width(), height);
898 x = next_x;
902 // Layout the right side buttons.
903 x = max_x + kButtonPadding;
905 // The overflow button.
906 overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
907 const bool show_overflow =
908 model_->loaded() &&
909 (model_->bookmark_bar_node()->child_count() > GetBookmarkButtonCount() ||
910 (GetBookmarkButtonCount() > 0 &&
911 !GetBookmarkButton(GetBookmarkButtonCount() - 1)->visible()));
912 overflow_button_->SetVisible(show_overflow);
913 x += overflow_pref.width();
915 // Separator.
916 if (bookmarks_separator_view_->visible()) {
917 bookmarks_separator_view_->SetBounds(x,
918 y - top_margin,
919 bookmarks_separator_pref.width(),
920 height + top_margin + kBottomMargin -
921 separator_margin);
923 x += bookmarks_separator_pref.width();
926 // The "Other Bookmarks" button.
927 if (other_bookmarks_button_->visible()) {
928 other_bookmarks_button_->SetBounds(x, y, other_bookmarks_pref.width(),
929 height);
930 x += other_bookmarks_pref.width() + kButtonPadding;
934 void BookmarkBarView::ViewHierarchyChanged(
935 const ViewHierarchyChangedDetails& details) {
936 if (details.is_add && details.child == this) {
937 // We may get inserted into a hierarchy with a profile - this typically
938 // occurs when the bar's contents get populated fast enough that the
939 // buttons are created before the bar is attached to a frame.
940 UpdateColors();
942 if (height() > 0) {
943 // We only layout while parented. When we become parented, if our bounds
944 // haven't changed, OnBoundsChanged() won't get invoked and we won't
945 // layout. Therefore we always force a layout when added.
946 Layout();
951 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas,
952 const views::CullSet& cull_set) {
953 View::PaintChildren(canvas, cull_set);
955 if (drop_info_.get() && drop_info_->valid &&
956 drop_info_->location.operation != 0 && drop_info_->location.index != -1 &&
957 drop_info_->location.button_type != DROP_OVERFLOW &&
958 !drop_info_->location.on) {
959 int index = drop_info_->location.index;
960 DCHECK(index <= GetBookmarkButtonCount());
961 int x = 0;
962 int y = 0;
963 int h = height();
964 if (index == GetBookmarkButtonCount()) {
965 if (index == 0) {
966 x = kLeftMargin;
967 } else {
968 x = GetBookmarkButton(index - 1)->x() +
969 GetBookmarkButton(index - 1)->width();
971 } else {
972 x = GetBookmarkButton(index)->x();
974 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->visible()) {
975 y = GetBookmarkButton(0)->y();
976 h = GetBookmarkButton(0)->height();
979 // Since the drop indicator is painted directly onto the canvas, we must
980 // make sure it is painted in the right location if the locale is RTL.
981 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
983 kDropIndicatorWidth,
985 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
987 // TODO(sky/glen): make me pretty!
988 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
992 bool BookmarkBarView::GetDropFormats(
993 int* formats,
994 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
995 if (!model_ || !model_->loaded())
996 return false;
997 *formats = ui::OSExchangeData::URL;
998 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat());
999 return true;
1002 bool BookmarkBarView::AreDropTypesRequired() {
1003 return true;
1006 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
1007 if (!model_ || !model_->loaded() ||
1008 !browser_->profile()->GetPrefs()->GetBoolean(
1009 bookmarks::prefs::kEditBookmarksEnabled))
1010 return false;
1012 if (!drop_info_.get())
1013 drop_info_.reset(new DropInfo());
1015 // Only accept drops of 1 node, which is the case for all data dragged from
1016 // bookmark bar and menus.
1017 return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
1020 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
1023 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
1024 if (!drop_info_.get())
1025 return 0;
1027 if (drop_info_->valid &&
1028 (drop_info_->x == event.x() && drop_info_->y == event.y())) {
1029 // The location of the mouse didn't change, return the last operation.
1030 return drop_info_->location.operation;
1033 drop_info_->x = event.x();
1034 drop_info_->y = event.y();
1036 DropLocation location;
1037 CalculateDropLocation(event, drop_info_->data, &location);
1039 if (drop_info_->valid && drop_info_->location.Equals(location)) {
1040 // The position we're going to drop didn't change, return the last drag
1041 // operation we calculated. Copy of the operation in case it changed.
1042 drop_info_->location.operation = location.operation;
1043 return drop_info_->location.operation;
1046 StopShowFolderDropMenuTimer();
1048 // TODO(sky): Optimize paint region.
1049 SchedulePaint();
1051 drop_info_->location = location;
1052 drop_info_->valid = true;
1054 if (drop_info_->is_menu_showing) {
1055 if (bookmark_drop_menu_)
1056 bookmark_drop_menu_->Cancel();
1057 drop_info_->is_menu_showing = false;
1060 if (location.on || location.button_type == DROP_OVERFLOW ||
1061 location.button_type == DROP_OTHER_FOLDER) {
1062 const BookmarkNode* node;
1063 if (location.button_type == DROP_OTHER_FOLDER)
1064 node = model_->other_node();
1065 else if (location.button_type == DROP_OVERFLOW)
1066 node = model_->bookmark_bar_node();
1067 else
1068 node = model_->bookmark_bar_node()->GetChild(location.index);
1069 StartShowFolderDropMenuTimer(node);
1072 return drop_info_->location.operation;
1075 void BookmarkBarView::OnDragExited() {
1076 StopShowFolderDropMenuTimer();
1078 // NOTE: we don't hide the menu on exit as it's possible the user moved the
1079 // mouse over the menu, which triggers an exit on us.
1081 drop_info_->valid = false;
1083 if (drop_info_->location.index != -1) {
1084 // TODO(sky): optimize the paint region.
1085 SchedulePaint();
1087 drop_info_.reset();
1090 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
1091 StopShowFolderDropMenuTimer();
1093 if (bookmark_drop_menu_)
1094 bookmark_drop_menu_->Cancel();
1096 if (!drop_info_.get() || !drop_info_->location.operation)
1097 return ui::DragDropTypes::DRAG_NONE;
1099 const BookmarkNode* root =
1100 (drop_info_->location.button_type == DROP_OTHER_FOLDER) ?
1101 model_->other_node() : model_->bookmark_bar_node();
1102 int index = drop_info_->location.index;
1104 if (index != -1) {
1105 // TODO(sky): optimize the SchedulePaint region.
1106 SchedulePaint();
1108 const BookmarkNode* parent_node;
1109 if (drop_info_->location.button_type == DROP_OTHER_FOLDER) {
1110 parent_node = root;
1111 index = parent_node->child_count();
1112 } else if (drop_info_->location.on) {
1113 parent_node = root->GetChild(index);
1114 index = parent_node->child_count();
1115 } else {
1116 parent_node = root;
1118 const BookmarkNodeData data = drop_info_->data;
1119 DCHECK(data.is_valid());
1120 bool copy = drop_info_->location.operation == ui::DragDropTypes::DRAG_COPY;
1121 drop_info_.reset();
1122 return chrome::DropBookmarks(
1123 browser_->profile(), data, parent_node, index, copy);
1126 void BookmarkBarView::OnThemeChanged() {
1127 UpdateColors();
1130 const char* BookmarkBarView::GetClassName() const {
1131 return kViewClassName;
1134 void BookmarkBarView::SetVisible(bool v) {
1135 if (v == visible())
1136 return;
1138 View::SetVisible(v);
1139 FOR_EACH_OBSERVER(BookmarkBarViewObserver, observers_,
1140 OnBookmarkBarVisibilityChanged());
1143 void BookmarkBarView::GetAccessibleState(ui::AXViewState* state) {
1144 state->role = ui::AX_ROLE_TOOLBAR;
1145 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
1148 void BookmarkBarView::AnimationProgressed(const gfx::Animation* animation) {
1149 // |browser_view_| can be NULL during tests.
1150 if (browser_view_)
1151 browser_view_->ToolbarSizeChanged(true);
1154 void BookmarkBarView::AnimationEnded(const gfx::Animation* animation) {
1155 // |browser_view_| can be NULL during tests.
1156 if (browser_view_) {
1157 browser_view_->ToolbarSizeChanged(false);
1158 SchedulePaint();
1162 void BookmarkBarView::BookmarkMenuControllerDeleted(
1163 BookmarkMenuController* controller) {
1164 if (controller == bookmark_menu_)
1165 bookmark_menu_ = NULL;
1166 else if (controller == bookmark_drop_menu_)
1167 bookmark_drop_menu_ = NULL;
1170 void BookmarkBarView::OnImportBookmarks() {
1171 int64 install_time = g_browser_process->metrics_service()->GetInstallDate();
1172 int64 time_from_install = base::Time::Now().ToTimeT() - install_time;
1173 if (bookmark_bar_state_ == BookmarkBar::SHOW) {
1174 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromBookmarkBarView",
1175 time_from_install);
1176 } else if (bookmark_bar_state_ == BookmarkBar::DETACHED) {
1177 UMA_HISTOGRAM_COUNTS("Import.ShowDialog.FromFloatingBookmarkBarView",
1178 time_from_install);
1181 chrome::ShowImportDialog(browser_);
1184 void BookmarkBarView::OnBookmarkBubbleShown(const GURL& url) {
1185 StopThrobbing(true);
1186 const BookmarkNode* node = model_->GetMostRecentlyAddedUserNodeForURL(url);
1187 if (!node)
1188 return; // Generally shouldn't happen.
1189 StartThrobbing(node, false);
1192 void BookmarkBarView::OnBookmarkBubbleHidden() {
1193 StopThrobbing(false);
1196 void BookmarkBarView::BookmarkModelLoaded(BookmarkModel* model,
1197 bool ids_reassigned) {
1198 // There should be no buttons. If non-zero it means Load was invoked more than
1199 // once, or we didn't properly clear things. Either of which shouldn't happen.
1200 // The actual bookmark buttons are added from Layout().
1201 DCHECK_EQ(0, GetBookmarkButtonCount());
1202 DCHECK(model->other_node());
1203 other_bookmarks_button_->SetAccessibleName(model->other_node()->GetTitle());
1204 other_bookmarks_button_->SetText(model->other_node()->GetTitle());
1205 managed_bookmarks_button_->SetAccessibleName(
1206 client_->managed_node()->GetTitle());
1207 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1208 UpdateColors();
1209 UpdateOtherAndManagedButtonsVisibility();
1210 other_bookmarks_button_->SetEnabled(true);
1211 managed_bookmarks_button_->SetEnabled(true);
1212 LayoutAndPaint();
1215 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
1216 NOTREACHED();
1217 // Do minimal cleanup, presumably we'll be deleted shortly.
1218 model_->RemoveObserver(this);
1219 model_ = NULL;
1222 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
1223 const BookmarkNode* old_parent,
1224 int old_index,
1225 const BookmarkNode* new_parent,
1226 int new_index) {
1227 bool was_throbbing = throbbing_view_ &&
1228 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
1229 if (was_throbbing)
1230 throbbing_view_->StopThrobbing();
1231 bool needs_layout_and_paint =
1232 BookmarkNodeRemovedImpl(model, old_parent, old_index);
1233 if (BookmarkNodeAddedImpl(model, new_parent, new_index))
1234 needs_layout_and_paint = true;
1235 if (was_throbbing && new_index < GetBookmarkButtonCount())
1236 StartThrobbing(new_parent->GetChild(new_index), false);
1237 if (needs_layout_and_paint)
1238 LayoutAndPaint();
1241 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
1242 const BookmarkNode* parent,
1243 int index) {
1244 if (BookmarkNodeAddedImpl(model, parent, index))
1245 LayoutAndPaint();
1248 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1249 const BookmarkNode* parent,
1250 int old_index,
1251 const BookmarkNode* node,
1252 const std::set<GURL>& removed_urls) {
1253 // Close the menu if the menu is showing for the deleted node.
1254 if (bookmark_menu_ && bookmark_menu_->node() == node)
1255 bookmark_menu_->Cancel();
1256 if (BookmarkNodeRemovedImpl(model, parent, old_index))
1257 LayoutAndPaint();
1260 void BookmarkBarView::BookmarkAllUserNodesRemoved(
1261 BookmarkModel* model,
1262 const std::set<GURL>& removed_urls) {
1263 UpdateOtherAndManagedButtonsVisibility();
1265 StopThrobbing(true);
1267 // Remove the existing buttons.
1268 while (GetBookmarkButtonCount())
1269 delete GetBookmarkButton(0);
1271 LayoutAndPaint();
1274 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1275 const BookmarkNode* node) {
1276 BookmarkNodeChangedImpl(model, node);
1279 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1280 const BookmarkNode* node) {
1281 if (node != model->bookmark_bar_node())
1282 return; // We only care about reordering of the bookmark bar node.
1284 // Remove the existing buttons.
1285 while (GetBookmarkButtonCount())
1286 delete child_at(0);
1288 // Create the new buttons.
1289 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1290 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1292 LayoutAndPaint();
1295 void BookmarkBarView::BookmarkNodeFaviconChanged(BookmarkModel* model,
1296 const BookmarkNode* node) {
1297 BookmarkNodeChangedImpl(model, node);
1300 void BookmarkBarView::WriteDragDataForView(View* sender,
1301 const gfx::Point& press_pt,
1302 ui::OSExchangeData* data) {
1303 content::RecordAction(UserMetricsAction("BookmarkBar_DragButton"));
1305 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1306 if (sender == GetBookmarkButton(i)) {
1307 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1308 const gfx::ImageSkia* icon = nullptr;
1309 if (node->is_url()) {
1310 const gfx::Image& image = model_->GetFavicon(node);
1311 icon = image.IsEmpty() ? GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1312 : image.ToImageSkia();
1313 } else {
1314 icon = GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER);
1317 button_drag_utils::SetDragImage(node->url(),
1318 node->GetTitle(),
1319 *icon,
1320 &press_pt,
1321 data,
1322 GetBookmarkButton(i)->GetWidget());
1323 WriteBookmarkDragData(node, data);
1324 return;
1327 NOTREACHED();
1330 int BookmarkBarView::GetDragOperationsForView(View* sender,
1331 const gfx::Point& p) {
1332 if (size_animation_->is_animating() ||
1333 (size_animation_->GetCurrentValue() == 0 &&
1334 bookmark_bar_state_ != BookmarkBar::DETACHED)) {
1335 // Don't let the user drag while animating open or we're closed (and not
1336 // detached, when detached size_animation_ is always 0). This typically is
1337 // only hit if the user does something to inadvertently trigger DnD such as
1338 // pressing the mouse and hitting control-b.
1339 return ui::DragDropTypes::DRAG_NONE;
1342 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1343 if (sender == GetBookmarkButton(i)) {
1344 return chrome::GetBookmarkDragOperation(
1345 browser_->profile(), model_->bookmark_bar_node()->GetChild(i));
1348 NOTREACHED();
1349 return ui::DragDropTypes::DRAG_NONE;
1352 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1353 const gfx::Point& press_pt,
1354 const gfx::Point& p) {
1355 // Check if we have not moved enough horizontally but we have moved downward
1356 // vertically - downward drag.
1357 gfx::Vector2d move_offset = p - press_pt;
1358 gfx::Vector2d horizontal_offset(move_offset.x(), 0);
1359 if (!View::ExceededDragThreshold(horizontal_offset) && move_offset.y() > 0) {
1360 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1361 if (sender == GetBookmarkButton(i)) {
1362 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1363 // If the folder button was dragged, show the menu instead.
1364 if (node && node->is_folder()) {
1365 views::MenuButton* menu_button =
1366 static_cast<views::MenuButton*>(sender);
1367 menu_button->Activate();
1368 return false;
1370 break;
1374 return true;
1377 void BookmarkBarView::OnMenuButtonClicked(views::View* view,
1378 const gfx::Point& point) {
1379 const BookmarkNode* node;
1381 int start_index = 0;
1382 if (view == other_bookmarks_button_) {
1383 node = model_->other_node();
1384 } else if (view == managed_bookmarks_button_) {
1385 node = client_->managed_node();
1386 } else if (view == overflow_button_) {
1387 node = model_->bookmark_bar_node();
1388 start_index = GetFirstHiddenNodeIndex();
1389 } else {
1390 int button_index = GetIndexOf(view);
1391 DCHECK_NE(-1, button_index);
1392 node = model_->bookmark_bar_node()->GetChild(button_index);
1395 RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
1396 bookmark_menu_ = new BookmarkMenuController(
1397 browser_, page_navigator_, GetWidget(), node, start_index, false);
1398 bookmark_menu_->set_observer(this);
1399 bookmark_menu_->RunMenuAt(this);
1402 void BookmarkBarView::ButtonPressed(views::Button* sender,
1403 const ui::Event& event) {
1404 WindowOpenDisposition disposition_from_event_flags =
1405 ui::DispositionFromEventFlags(event.flags());
1407 if (sender->tag() == kAppsShortcutButtonTag) {
1408 OpenURLParams params(GURL(chrome::kChromeUIAppsURL),
1409 Referrer(),
1410 disposition_from_event_flags,
1411 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
1412 false);
1413 page_navigator_->OpenURL(params);
1414 RecordBookmarkAppsPageOpen(GetBookmarkLaunchLocation());
1415 return;
1418 const BookmarkNode* node;
1419 if (sender->tag() == kOtherFolderButtonTag) {
1420 node = model_->other_node();
1421 } else if (sender->tag() == kManagedFolderButtonTag) {
1422 node = client_->managed_node();
1423 } else {
1424 int index = GetIndexOf(sender);
1425 DCHECK_NE(-1, index);
1426 node = model_->bookmark_bar_node()->GetChild(index);
1428 DCHECK(page_navigator_);
1430 if (node->is_url()) {
1431 RecordAppLaunch(browser_->profile(), node->url());
1432 OpenURLParams params(
1433 node->url(), Referrer(), disposition_from_event_flags,
1434 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
1435 page_navigator_->OpenURL(params);
1436 } else {
1437 chrome::OpenAll(GetWidget()->GetNativeWindow(), page_navigator_, node,
1438 disposition_from_event_flags, browser_->profile());
1441 RecordBookmarkLaunch(node, GetBookmarkLaunchLocation());
1444 void BookmarkBarView::ShowContextMenuForView(views::View* source,
1445 const gfx::Point& point,
1446 ui::MenuSourceType source_type) {
1447 if (!model_->loaded()) {
1448 // Don't do anything if the model isn't loaded.
1449 return;
1452 const BookmarkNode* parent = NULL;
1453 std::vector<const BookmarkNode*> nodes;
1454 if (source == other_bookmarks_button_) {
1455 parent = model_->other_node();
1456 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1457 // sure the user can't edit/delete the node in this case.
1458 nodes.push_back(parent);
1459 } else if (source == managed_bookmarks_button_) {
1460 parent = client_->managed_node();
1461 nodes.push_back(parent);
1462 } else if (source != this && source != apps_page_shortcut_) {
1463 // User clicked on one of the bookmark buttons, find which one they
1464 // clicked on, except for the apps page shortcut, which must behave as if
1465 // the user clicked on the bookmark bar background.
1466 int bookmark_button_index = GetIndexOf(source);
1467 DCHECK(bookmark_button_index != -1 &&
1468 bookmark_button_index < GetBookmarkButtonCount());
1469 const BookmarkNode* node =
1470 model_->bookmark_bar_node()->GetChild(bookmark_button_index);
1471 nodes.push_back(node);
1472 parent = node->parent();
1473 } else {
1474 parent = model_->bookmark_bar_node();
1475 nodes.push_back(parent);
1477 bool close_on_remove =
1478 (parent == model_->other_node()) && (parent->child_count() == 1);
1480 context_menu_.reset(new BookmarkContextMenu(
1481 GetWidget(), browser_, browser_->profile(),
1482 browser_->tab_strip_model()->GetActiveWebContents(),
1483 parent, nodes, close_on_remove));
1484 context_menu_->RunMenuAt(point, source_type);
1487 void BookmarkBarView::Init() {
1488 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
1489 // return NULL. When we're inserted into a hierarchy, we'll call
1490 // UpdateColors(), which will set the appropriate colors for all the objects
1491 // added in this function.
1493 // Child views are traversed in the order they are added. Make sure the order
1494 // they are added matches the visual order.
1495 overflow_button_ = CreateOverflowButton();
1496 AddChildView(overflow_button_);
1498 other_bookmarks_button_ = CreateOtherBookmarksButton();
1499 // We'll re-enable when the model is loaded.
1500 other_bookmarks_button_->SetEnabled(false);
1501 AddChildView(other_bookmarks_button_);
1503 managed_bookmarks_button_ = CreateManagedBookmarksButton();
1504 // Also re-enabled when the model is loaded.
1505 managed_bookmarks_button_->SetEnabled(false);
1506 AddChildView(managed_bookmarks_button_);
1508 apps_page_shortcut_ = CreateAppsPageShortcutButton();
1509 AddChildView(apps_page_shortcut_);
1510 profile_pref_registrar_.Init(browser_->profile()->GetPrefs());
1511 profile_pref_registrar_.Add(
1512 bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1513 base::Bind(&BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged,
1514 base::Unretained(this)));
1515 profile_pref_registrar_.Add(
1516 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
1517 base::Bind(&BookmarkBarView::OnShowManagedBookmarksPrefChanged,
1518 base::Unretained(this)));
1519 apps_page_shortcut_->SetVisible(
1520 chrome::ShouldShowAppsShortcutInBookmarkBar(
1521 browser_->profile(), browser_->host_desktop_type()));
1523 bookmarks_separator_view_ = new ButtonSeparatorView();
1524 AddChildView(bookmarks_separator_view_);
1525 UpdateBookmarksSeparatorVisibility();
1527 instructions_ = new BookmarkBarInstructionsView(this);
1528 AddChildView(instructions_);
1530 set_context_menu_controller(this);
1532 size_animation_.reset(new gfx::SlideAnimation(this));
1534 model_ = BookmarkModelFactory::GetForProfile(browser_->profile());
1535 client_ = ChromeBookmarkClientFactory::GetForProfile(browser_->profile());
1536 if (model_) {
1537 model_->AddObserver(this);
1538 if (model_->loaded())
1539 BookmarkModelLoaded(model_, false);
1540 // else case: we'll receive notification back from the BookmarkModel when
1541 // done loading, then we'll populate the bar.
1545 int BookmarkBarView::GetBookmarkButtonCount() const {
1546 // We contain six non-bookmark button views: managed bookmarks,
1547 // other bookmarks, bookmarks separator, chevrons (for overflow), apps page,
1548 // and the instruction label.
1549 return child_count() - 6;
1552 views::LabelButton* BookmarkBarView::GetBookmarkButton(int index) {
1553 // CHECK as otherwise we may do the wrong cast.
1554 CHECK(index >= 0 && index < GetBookmarkButtonCount());
1555 return static_cast<views::LabelButton*>(child_at(index));
1558 BookmarkLaunchLocation BookmarkBarView::GetBookmarkLaunchLocation() const {
1559 return IsDetached() ? BOOKMARK_LAUNCH_LOCATION_DETACHED_BAR :
1560 BOOKMARK_LAUNCH_LOCATION_ATTACHED_BAR;
1563 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1564 const int bb_count = GetBookmarkButtonCount();
1565 for (int i = 0; i < bb_count; ++i) {
1566 if (!GetBookmarkButton(i)->visible())
1567 return i;
1569 return bb_count;
1572 MenuButton* BookmarkBarView::CreateOtherBookmarksButton() {
1573 // Title is set in Loaded.
1574 MenuButton* button =
1575 new BookmarkFolderButton(this, base::string16(), this, false);
1576 button->set_id(VIEW_ID_OTHER_BOOKMARKS);
1577 button->SetImage(views::Button::STATE_NORMAL,
1578 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER));
1579 button->set_context_menu_controller(this);
1580 button->set_tag(kOtherFolderButtonTag);
1581 return button;
1584 MenuButton* BookmarkBarView::CreateManagedBookmarksButton() {
1585 // Title is set in Loaded.
1586 MenuButton* button =
1587 new BookmarkFolderButton(this, base::string16(), this, false);
1588 button->set_id(VIEW_ID_MANAGED_BOOKMARKS);
1589 button->SetImage(views::Button::STATE_NORMAL,
1590 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER_MANAGED));
1591 button->set_context_menu_controller(this);
1592 button->set_tag(kManagedFolderButtonTag);
1593 return button;
1596 MenuButton* BookmarkBarView::CreateOverflowButton() {
1597 MenuButton* button = new OverFlowButton(this);
1598 button->SetImage(views::Button::STATE_NORMAL,
1599 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_CHEVRONS));
1601 // The overflow button's image contains an arrow and therefore it is a
1602 // direction sensitive image and we need to flip it if the UI layout is
1603 // right-to-left.
1605 // By default, menu buttons are not flipped because they generally contain
1606 // text and flipping the gfx::Canvas object will break text rendering. Since
1607 // the overflow button does not contain text, we can safely flip it.
1608 button->EnableCanvasFlippingForRTLUI(true);
1610 // Make visible as necessary.
1611 button->SetVisible(false);
1612 // Set accessibility name.
1613 button->SetAccessibleName(
1614 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
1615 return button;
1618 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1619 if (node->is_url()) {
1620 BookmarkButton* button = new BookmarkButton(
1621 this, node->url(), node->GetTitle(), browser_->profile());
1622 ConfigureButton(node, button);
1623 return button;
1625 views::MenuButton* button =
1626 new BookmarkFolderButton(this, node->GetTitle(), this, false);
1627 button->SetImage(views::Button::STATE_NORMAL,
1628 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER));
1629 ConfigureButton(node, button);
1630 return button;
1633 views::LabelButton* BookmarkBarView::CreateAppsPageShortcutButton() {
1634 views::LabelButton* button = new ShortcutButton(
1635 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_APPS_SHORTCUT_NAME));
1636 button->SetTooltipText(l10n_util::GetStringUTF16(
1637 IDS_BOOKMARK_BAR_APPS_SHORTCUT_TOOLTIP));
1638 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1639 button->SetImage(views::Button::STATE_NORMAL,
1640 *GetImageSkiaNamed(IDR_BOOKMARK_BAR_APPS_SHORTCUT));
1641 button->set_context_menu_controller(this);
1642 button->set_tag(kAppsShortcutButtonTag);
1643 return button;
1646 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1647 views::LabelButton* button) {
1648 button->SetText(node->GetTitle());
1649 button->SetAccessibleName(node->GetTitle());
1650 button->set_id(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1651 // We don't always have a theme provider (ui tests, for example).
1652 if (GetThemeProvider()) {
1653 button->SetTextColor(
1654 views::Button::STATE_NORMAL,
1655 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
1658 button->SetMinSize(gfx::Size());
1659 button->set_context_menu_controller(this);
1660 button->set_drag_controller(this);
1661 if (node->is_url()) {
1662 const gfx::Image& favicon = model_->GetFavicon(node);
1663 button->SetImage(views::Button::STATE_NORMAL,
1664 favicon.IsEmpty() ? *GetImageSkiaNamed(IDR_DEFAULT_FAVICON)
1665 : *favicon.ToImageSkia());
1667 button->SetMaxSize(gfx::Size(kMaxButtonWidth, 0));
1670 bool BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
1671 const BookmarkNode* parent,
1672 int index) {
1673 const bool needs_layout_and_paint = UpdateOtherAndManagedButtonsVisibility();
1674 if (parent != model->bookmark_bar_node())
1675 return needs_layout_and_paint;
1676 if (index < GetBookmarkButtonCount()) {
1677 const BookmarkNode* node = parent->GetChild(index);
1678 AddChildViewAt(CreateBookmarkButton(node), index);
1679 return true;
1681 // If the new node was added after the last button we've created we may be
1682 // able to fit it. Assume we can by returning true, which forces a Layout()
1683 // and creation of the button (if it fits).
1684 return index == GetBookmarkButtonCount();
1687 bool BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1688 const BookmarkNode* parent,
1689 int index) {
1690 const bool needs_layout = UpdateOtherAndManagedButtonsVisibility();
1692 StopThrobbing(true);
1693 // No need to start throbbing again as the bookmark bubble can't be up at
1694 // the same time as the user reorders.
1696 if (parent != model->bookmark_bar_node()) {
1697 // Only children of the bookmark_bar_node get buttons.
1698 return needs_layout;
1700 if (index >= GetBookmarkButtonCount())
1701 return needs_layout;
1703 delete child_at(index);
1704 return true;
1707 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1708 const BookmarkNode* node) {
1709 if (node == client_->managed_node()) {
1710 // The managed node may have its title updated.
1711 managed_bookmarks_button_->SetAccessibleName(
1712 client_->managed_node()->GetTitle());
1713 managed_bookmarks_button_->SetText(client_->managed_node()->GetTitle());
1714 return;
1717 if (node->parent() != model->bookmark_bar_node()) {
1718 // We only care about nodes on the bookmark bar.
1719 return;
1721 int index = model->bookmark_bar_node()->GetIndexOf(node);
1722 DCHECK_NE(-1, index);
1723 if (index >= GetBookmarkButtonCount())
1724 return; // Buttons are created as needed.
1725 views::LabelButton* button = GetBookmarkButton(index);
1726 const int old_pref_width = button->GetPreferredSize().width();
1727 ConfigureButton(node, button);
1728 if (old_pref_width != button->GetPreferredSize().width())
1729 LayoutAndPaint();
1732 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1733 if (bookmark_drop_menu_) {
1734 if (bookmark_drop_menu_->node() == node) {
1735 // Already showing for the specified node.
1736 return;
1738 bookmark_drop_menu_->Cancel();
1741 views::MenuButton* menu_button = GetMenuButtonForNode(node);
1742 if (!menu_button)
1743 return;
1745 int start_index = 0;
1746 if (node == model_->bookmark_bar_node())
1747 start_index = GetFirstHiddenNodeIndex();
1749 drop_info_->is_menu_showing = true;
1750 bookmark_drop_menu_ = new BookmarkMenuController(
1751 browser_, page_navigator_, GetWidget(), node, start_index, true);
1752 bookmark_drop_menu_->set_observer(this);
1753 bookmark_drop_menu_->RunMenuAt(this);
1756 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1757 show_folder_method_factory_.InvalidateWeakPtrs();
1760 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1761 if (!animations_enabled) {
1762 // So that tests can run as fast as possible disable the delay during
1763 // testing.
1764 ShowDropFolderForNode(node);
1765 return;
1767 show_folder_method_factory_.InvalidateWeakPtrs();
1768 base::MessageLoop::current()->PostDelayedTask(
1769 FROM_HERE,
1770 base::Bind(&BookmarkBarView::ShowDropFolderForNode,
1771 show_folder_method_factory_.GetWeakPtr(),
1772 node),
1773 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
1776 void BookmarkBarView::CalculateDropLocation(const DropTargetEvent& event,
1777 const BookmarkNodeData& data,
1778 DropLocation* location) {
1779 DCHECK(model_);
1780 DCHECK(model_->loaded());
1781 DCHECK(data.is_valid());
1783 *location = DropLocation();
1785 // The drop event uses the screen coordinates while the child Views are
1786 // always laid out from left to right (even though they are rendered from
1787 // right-to-left on RTL locales). Thus, in order to make sure the drop
1788 // coordinates calculation works, we mirror the event's X coordinate if the
1789 // locale is RTL.
1790 int mirrored_x = GetMirroredXInView(event.x());
1792 bool found = false;
1793 const int other_delta_x = mirrored_x - other_bookmarks_button_->x();
1794 Profile* profile = browser_->profile();
1795 if (other_bookmarks_button_->visible() && other_delta_x >= 0 &&
1796 other_delta_x < other_bookmarks_button_->width()) {
1797 // Mouse is over 'other' folder.
1798 location->button_type = DROP_OTHER_FOLDER;
1799 location->on = true;
1800 found = true;
1801 } else if (!GetBookmarkButtonCount()) {
1802 // No bookmarks, accept the drop.
1803 location->index = 0;
1804 const BookmarkNode* node = data.GetFirstNode(model_, profile->GetPath());
1805 int ops = node && client_->CanBeEditedByUser(node) ?
1806 ui::DragDropTypes::DRAG_MOVE :
1807 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
1808 location->operation = chrome::GetPreferredBookmarkDropOperation(
1809 event.source_operations(), ops);
1810 return;
1813 for (int i = 0; i < GetBookmarkButtonCount() &&
1814 GetBookmarkButton(i)->visible() && !found; i++) {
1815 views::LabelButton* button = GetBookmarkButton(i);
1816 int button_x = mirrored_x - button->x();
1817 int button_w = button->width();
1818 if (button_x < button_w) {
1819 found = true;
1820 const BookmarkNode* node = model_->bookmark_bar_node()->GetChild(i);
1821 if (node->is_folder()) {
1822 if (button_x <= views::kDropBetweenPixels) {
1823 location->index = i;
1824 } else if (button_x < button_w - views::kDropBetweenPixels) {
1825 location->index = i;
1826 location->on = true;
1827 } else {
1828 location->index = i + 1;
1830 } else if (button_x < button_w / 2) {
1831 location->index = i;
1832 } else {
1833 location->index = i + 1;
1835 break;
1839 if (!found) {
1840 if (overflow_button_->visible()) {
1841 // Are we over the overflow button?
1842 int overflow_delta_x = mirrored_x - overflow_button_->x();
1843 if (overflow_delta_x >= 0 &&
1844 overflow_delta_x < overflow_button_->width()) {
1845 // Mouse is over overflow button.
1846 location->index = GetFirstHiddenNodeIndex();
1847 location->button_type = DROP_OVERFLOW;
1848 } else if (overflow_delta_x < 0) {
1849 // Mouse is after the last visible button but before overflow button;
1850 // use the last visible index.
1851 location->index = GetFirstHiddenNodeIndex();
1852 } else {
1853 return;
1855 } else if (!other_bookmarks_button_->visible() ||
1856 mirrored_x < other_bookmarks_button_->x()) {
1857 // Mouse is after the last visible button but before more recently
1858 // bookmarked; use the last visible index.
1859 location->index = GetFirstHiddenNodeIndex();
1860 } else {
1861 return;
1865 if (location->on) {
1866 const BookmarkNode* parent = (location->button_type == DROP_OTHER_FOLDER) ?
1867 model_->other_node() :
1868 model_->bookmark_bar_node()->GetChild(location->index);
1869 location->operation = chrome::GetBookmarkDropOperation(
1870 profile, event, data, parent, parent->child_count());
1871 if (!location->operation && !data.has_single_url() &&
1872 data.GetFirstNode(model_, profile->GetPath()) == parent) {
1873 // Don't open a menu if the node being dragged is the menu to open.
1874 location->on = false;
1876 } else {
1877 location->operation = chrome::GetBookmarkDropOperation(
1878 profile, event, data, model_->bookmark_bar_node(), location->index);
1882 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
1883 ui::OSExchangeData* data) {
1884 DCHECK(node && data);
1885 BookmarkNodeData drag_data(node);
1886 drag_data.Write(browser_->profile()->GetPath(), data);
1889 void BookmarkBarView::StartThrobbing(const BookmarkNode* node,
1890 bool overflow_only) {
1891 DCHECK(!throbbing_view_);
1893 // Determine which visible button is showing the bookmark (or is an ancestor
1894 // of the bookmark).
1895 const BookmarkNode* bbn = model_->bookmark_bar_node();
1896 const BookmarkNode* parent_on_bb = node;
1897 while (parent_on_bb) {
1898 const BookmarkNode* parent = parent_on_bb->parent();
1899 if (parent == bbn)
1900 break;
1901 parent_on_bb = parent;
1903 if (parent_on_bb) {
1904 int index = bbn->GetIndexOf(parent_on_bb);
1905 if (index >= GetFirstHiddenNodeIndex()) {
1906 // Node is hidden, animate the overflow button.
1907 throbbing_view_ = overflow_button_;
1908 } else if (!overflow_only) {
1909 throbbing_view_ = static_cast<CustomButton*>(child_at(index));
1911 } else if (client_->IsDescendantOfManagedNode(node)) {
1912 throbbing_view_ = managed_bookmarks_button_;
1913 } else if (!overflow_only) {
1914 throbbing_view_ = other_bookmarks_button_;
1917 // Use a large number so that the button continues to throb.
1918 if (throbbing_view_)
1919 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
1922 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove(
1923 const BookmarkNode* parent,
1924 int old_index) {
1925 const BookmarkNode* bbn = model_->bookmark_bar_node();
1926 const BookmarkNode* old_node = parent;
1927 int old_index_on_bb = old_index;
1928 while (old_node && old_node != bbn) {
1929 const BookmarkNode* parent = old_node->parent();
1930 if (parent == bbn) {
1931 old_index_on_bb = bbn->GetIndexOf(old_node);
1932 break;
1934 old_node = parent;
1936 if (old_node) {
1937 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) {
1938 // Node is hidden, animate the overflow button.
1939 return overflow_button_;
1941 return static_cast<CustomButton*>(child_at(old_index_on_bb));
1943 if (client_->IsDescendantOfManagedNode(parent))
1944 return managed_bookmarks_button_;
1945 // Node wasn't on the bookmark bar, use the "Other Bookmarks" button.
1946 return other_bookmarks_button_;
1949 void BookmarkBarView::UpdateColors() {
1950 // We don't always have a theme provider (ui tests, for example).
1951 const ui::ThemeProvider* theme_provider = GetThemeProvider();
1952 if (!theme_provider)
1953 return;
1954 SkColor color =
1955 theme_provider->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
1956 for (int i = 0; i < GetBookmarkButtonCount(); ++i)
1957 GetBookmarkButton(i)->SetTextColor(views::Button::STATE_NORMAL, color);
1958 other_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color);
1959 managed_bookmarks_button_->SetTextColor(views::Button::STATE_NORMAL, color);
1960 if (apps_page_shortcut_->visible())
1961 apps_page_shortcut_->SetTextColor(views::Button::STATE_NORMAL, color);
1964 bool BookmarkBarView::UpdateOtherAndManagedButtonsVisibility() {
1965 bool has_other_children = !model_->other_node()->empty();
1966 bool update_other = has_other_children != other_bookmarks_button_->visible();
1967 if (update_other) {
1968 other_bookmarks_button_->SetVisible(has_other_children);
1969 UpdateBookmarksSeparatorVisibility();
1972 bool show_managed = !client_->managed_node()->empty() &&
1973 browser_->profile()->GetPrefs()->GetBoolean(
1974 bookmarks::prefs::kShowManagedBookmarksInBookmarkBar);
1975 bool update_managed = show_managed != managed_bookmarks_button_->visible();
1976 if (update_managed)
1977 managed_bookmarks_button_->SetVisible(show_managed);
1979 return update_other || update_managed;
1982 void BookmarkBarView::UpdateBookmarksSeparatorVisibility() {
1983 // Ash does not paint the bookmarks separator line because it looks odd on
1984 // the flat background. We keep it present for layout, but don't draw it.
1985 bookmarks_separator_view_->SetVisible(
1986 browser_->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH &&
1987 other_bookmarks_button_->visible());
1990 void BookmarkBarView::OnAppsPageShortcutVisibilityPrefChanged() {
1991 DCHECK(apps_page_shortcut_);
1992 // Only perform layout if required.
1993 bool visible = chrome::ShouldShowAppsShortcutInBookmarkBar(
1994 browser_->profile(), browser_->host_desktop_type());
1995 if (apps_page_shortcut_->visible() == visible)
1996 return;
1997 apps_page_shortcut_->SetVisible(visible);
1998 UpdateBookmarksSeparatorVisibility();
1999 LayoutAndPaint();
2002 void BookmarkBarView::OnShowManagedBookmarksPrefChanged() {
2003 if (UpdateOtherAndManagedButtonsVisibility())
2004 LayoutAndPaint();