1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <set>
10 #include <vector>
11
12 #include "base/i18n/rtl.h"
13 #include "base/metrics/histogram.h"
14 #include "base/string_util.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/bookmarks/bookmark_model.h"
17 #include "chrome/browser/bookmarks/bookmark_utils.h"
18 #include "chrome/browser/browser_shutdown.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/metrics/user_metrics.h"
21 #include "chrome/browser/prefs/pref_service.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/sync/sync_ui_util.h"
24 #include "chrome/browser/themes/theme_service.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/view_ids.h"
27 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
28 #include "chrome/browser/ui/views/event_utils.h"
29 #include "chrome/browser/ui/views/frame/browser_view.h"
30 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/extensions/extension_constants.h"
33 #include "chrome/common/pref_names.h"
34 #include "content/browser/renderer_host/render_view_host.h"
35 #include "content/browser/renderer_host/render_widget_host_view.h"
36 #include "content/browser/tab_contents/page_navigator.h"
37 #include "content/browser/tab_contents/tab_contents.h"
38 #include "content/common/notification_service.h"
39 #include "content/common/page_transition_types.h"
40 #include "grit/app_resources.h"
41 #include "grit/generated_resources.h"
42 #include "grit/theme_resources.h"
43 #include "ui/base/accessibility/accessible_view_state.h"
44 #include "ui/base/animation/slide_animation.h"
45 #include "ui/base/dragdrop/os_exchange_data.h"
46 #include "ui/base/l10n/l10n_util.h"
47 #include "ui/base/resource/resource_bundle.h"
48 #include "ui/base/text/text_elider.h"
49 #include "ui/gfx/canvas_skia.h"
50 #include "views/controls/button/menu_button.h"
51 #include "views/controls/label.h"
52 #include "views/controls/menu/menu_item_view.h"
53 #include "views/drag_utils.h"
54 #include "views/metrics.h"
55 #include "views/view_constants.h"
56 #include "views/widget/tooltip_manager.h"
57 #include "views/widget/widget.h"
58 #include "views/window/window.h"
59
60 using views::CustomButton;
61 using views::DropTargetEvent;
62 using views::MenuButton;
63 using views::MenuItemView;
64 using views::View;
65
66 // How much we want the bookmark bar to overlap the toolbar when in its
67 // 'always shown' mode.
68 static const int kToolbarOverlap = 3;
69
70 // Margins around the content.
71 static const int kDetachedTopMargin = 1; // When attached, we use 0 and let the
72 // toolbar above serve as the margin.
73 static const int kBottomMargin = 2;
74 static const int kLeftMargin = 1;
75 static const int kRightMargin = 1;
76
77 // Preferred height of the bookmarks bar.
78 static const int kBarHeight = 28;
79
80 // Preferred height of the bookmarks bar when only shown on the new tab page.
81 const int BookmarkBarView::kNewtabBarHeight = 57;
82
83 // Padding between buttons.
84 static const int kButtonPadding = 0;
85
86 // Command ids used in the menu allowing the user to choose when we're visible.
87 static const int kAlwaysShowCommandID = 1;
88
89 // Icon to display when one isn't found for the page.
90 static SkBitmap* kDefaultFavicon = NULL;
91
92 // Icon used for folders.
93 static SkBitmap* kFolderIcon = NULL;
94
95 // Offset for where the menu is shown relative to the bottom of the
96 // BookmarkBarView.
97 static const int kMenuOffset = 3;
98
99 // Color of the drop indicator.
100 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
101
102 // Width of the drop indicator.
103 static const int kDropIndicatorWidth = 2;
104
105 // Distance between the bottom of the bar and the separator.
106 static const int kSeparatorMargin = 1;
107
108 // Width of the separator between the recently bookmarked button and the
109 // overflow indicator.
110 static const int kSeparatorWidth = 4;
111
112 // Starting x-coordinate of the separator line within a separator.
113 static const int kSeparatorStartX = 2;
114
115 // Left-padding for the instructional text.
116 static const int kInstructionsPadding = 6;
117
118 // Tag for the 'Other bookmarks' button.
119 static const int kOtherFolderButtonTag = 1;
120
121 // Tag for the sync error button.
122 static const int kSyncErrorButtonTag = 2;
123
124 namespace {
125
126 // BookmarkButton -------------------------------------------------------------
127
128 // Buttons used for the bookmarks on the bookmark bar.
129
130 class BookmarkButton : public views::TextButton {
131 public:
BookmarkButton(views::ButtonListener * listener,const GURL & url,const std::wstring & title,Profile * profile)132 BookmarkButton(views::ButtonListener* listener,
133 const GURL& url,
134 const std::wstring& title,
135 Profile* profile)
136 : TextButton(listener, title),
137 url_(url),
138 profile_(profile) {
139 show_animation_.reset(new ui::SlideAnimation(this));
140 if (BookmarkBarView::testing_) {
141 // For some reason during testing the events generated by animating
142 // throw off the test. So, don't animate while testing.
143 show_animation_->Reset(1);
144 } else {
145 show_animation_->Show();
146 }
147 }
148
GetTooltipText(const gfx::Point & p,std::wstring * tooltip)149 bool GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
150 gfx::Point location(p);
151 ConvertPointToScreen(this, &location);
152 *tooltip = BookmarkBarView::CreateToolTipForURLAndTitle(location, url_,
153 text(), profile_);
154 return !tooltip->empty();
155 }
156
IsTriggerableEvent(const views::MouseEvent & e)157 virtual bool IsTriggerableEvent(const views::MouseEvent& e) {
158 return event_utils::IsPossibleDispositionEvent(e);
159 }
160
161 private:
162 const GURL& url_;
163 Profile* profile_;
164 scoped_ptr<ui::SlideAnimation> show_animation_;
165
166 DISALLOW_COPY_AND_ASSIGN(BookmarkButton);
167 };
168
169 // BookmarkFolderButton -------------------------------------------------------
170
171 // Buttons used for folders on the bookmark bar, including the 'other folders'
172 // button.
173 class BookmarkFolderButton : public views::MenuButton {
174 public:
BookmarkFolderButton(views::ButtonListener * listener,const std::wstring & title,views::ViewMenuDelegate * menu_delegate,bool show_menu_marker)175 BookmarkFolderButton(views::ButtonListener* listener,
176 const std::wstring& title,
177 views::ViewMenuDelegate* menu_delegate,
178 bool show_menu_marker)
179 : MenuButton(listener, title, menu_delegate, show_menu_marker) {
180 show_animation_.reset(new ui::SlideAnimation(this));
181 if (BookmarkBarView::testing_) {
182 // For some reason during testing the events generated by animating
183 // throw off the test. So, don't animate while testing.
184 show_animation_->Reset(1);
185 } else {
186 show_animation_->Show();
187 }
188 }
189
IsTriggerableEvent(const views::MouseEvent & e)190 virtual bool IsTriggerableEvent(const views::MouseEvent& e) {
191 // Left clicks should show the menu contents and right clicks should show
192 // the context menu. They should not trigger the opening of underlying urls.
193 if (e.flags() == ui::EF_LEFT_BUTTON_DOWN ||
194 e.flags() == ui::EF_RIGHT_BUTTON_DOWN)
195 return false;
196
197 WindowOpenDisposition disposition(
198 event_utils::DispositionFromEventFlags(e.flags()));
199 return disposition != CURRENT_TAB;
200 }
201
OnPaint(gfx::Canvas * canvas)202 virtual void OnPaint(gfx::Canvas* canvas) {
203 views::MenuButton::PaintButton(canvas, views::MenuButton::PB_NORMAL);
204 }
205
206 private:
207 scoped_ptr<ui::SlideAnimation> show_animation_;
208
209 DISALLOW_COPY_AND_ASSIGN(BookmarkFolderButton);
210 };
211
212 // OverFlowButton (chevron) --------------------------------------------------
213
214 class OverFlowButton : public views::MenuButton {
215 public:
OverFlowButton(BookmarkBarView * owner)216 explicit OverFlowButton(BookmarkBarView* owner)
217 : MenuButton(NULL, std::wstring(), owner, false),
218 owner_(owner) {}
219
OnMousePressed(const views::MouseEvent & e)220 virtual bool OnMousePressed(const views::MouseEvent& e) {
221 owner_->StopThrobbing(true);
222 return views::MenuButton::OnMousePressed(e);
223 }
224
225 private:
226 BookmarkBarView* owner_;
227
228 DISALLOW_COPY_AND_ASSIGN(OverFlowButton);
229 };
230
RecordAppLaunch(Profile * profile,GURL url)231 void RecordAppLaunch(Profile* profile, GURL url) {
232 DCHECK(profile->GetExtensionService());
233 if (!profile->GetExtensionService()->IsInstalledApp(url))
234 return;
235
236 UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
237 extension_misc::APP_LAUNCH_BOOKMARK_BAR,
238 extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
239 }
240
241 } // namespace
242
243 // DropInfo -------------------------------------------------------------------
244
245 // Tracks drops on the BookmarkBarView.
246
247 struct BookmarkBarView::DropInfo {
DropInfoBookmarkBarView::DropInfo248 DropInfo()
249 : valid(false),
250 drop_index(-1),
251 is_menu_showing(false),
252 drop_on(false),
253 is_over_overflow(false),
254 is_over_other(false),
255 x(0),
256 y(0),
257 drag_operation(0) {
258 }
259
260 // Whether the data is valid.
261 bool valid;
262
263 // Index into the model the drop is over. This is relative to the root node.
264 int drop_index;
265
266 // If true, the menu is being shown.
267 bool is_menu_showing;
268
269 // If true, the user is dropping on a node. This is only used for folder
270 // nodes.
271 bool drop_on;
272
273 // If true, the user is over the overflow button.
274 bool is_over_overflow;
275
276 // If true, the user is over the other button.
277 bool is_over_other;
278
279 // Coordinates of the drag (in terms of the BookmarkBarView).
280 int x;
281 int y;
282
283 // The current drag operation.
284 int drag_operation;
285
286 // DropData for the drop.
287 BookmarkNodeData data;
288 };
289
290 // ButtonSeparatorView --------------------------------------------------------
291
292 class BookmarkBarView::ButtonSeparatorView : public views::View {
293 public:
ButtonSeparatorView()294 ButtonSeparatorView() {}
~ButtonSeparatorView()295 virtual ~ButtonSeparatorView() {}
296
OnPaint(gfx::Canvas * canvas)297 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
298 DetachableToolbarView::PaintVerticalDivider(
299 canvas, kSeparatorStartX, height(), 1,
300 DetachableToolbarView::kEdgeDividerColor,
301 DetachableToolbarView::kMiddleDividerColor,
302 GetThemeProvider()->GetColor(ThemeService::COLOR_TOOLBAR));
303 }
304
GetPreferredSize()305 virtual gfx::Size GetPreferredSize() OVERRIDE {
306 // We get the full height of the bookmark bar, so that the height returned
307 // here doesn't matter.
308 return gfx::Size(kSeparatorWidth, 1);
309 }
310
GetAccessibleState(ui::AccessibleViewState * state)311 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
312 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_SEPARATOR);
313 state->role = ui::AccessibilityTypes::ROLE_SEPARATOR;
314 }
315
316 private:
317 DISALLOW_COPY_AND_ASSIGN(ButtonSeparatorView);
318 };
319
320 // BookmarkBarView ------------------------------------------------------------
321
322 // static
323 const int BookmarkBarView::kMaxButtonWidth = 150;
324 const int BookmarkBarView::kNewtabHorizontalPadding = 8;
325 const int BookmarkBarView::kNewtabVerticalPadding = 12;
326
327 // static
328 bool BookmarkBarView::testing_ = false;
329
330 // Returns the bitmap to use for starred folders.
GetFolderIcon()331 static const SkBitmap& GetFolderIcon() {
332 if (!kFolderIcon) {
333 kFolderIcon = ResourceBundle::GetSharedInstance().
334 GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER);
335 }
336 return *kFolderIcon;
337 }
338
BookmarkBarView(Profile * profile,Browser * browser)339 BookmarkBarView::BookmarkBarView(Profile* profile, Browser* browser)
340 : profile_(NULL),
341 page_navigator_(NULL),
342 model_(NULL),
343 bookmark_menu_(NULL),
344 bookmark_drop_menu_(NULL),
345 other_bookmarked_button_(NULL),
346 model_changed_listener_(NULL),
347 show_folder_drop_menu_task_(NULL),
348 sync_error_button_(NULL),
349 sync_service_(NULL),
350 overflow_button_(NULL),
351 instructions_(NULL),
352 bookmarks_separator_view_(NULL),
353 browser_(browser),
354 infobar_visible_(false),
355 throbbing_view_(NULL) {
356 if (profile->GetProfileSyncService()) {
357 // Obtain a pointer to the profile sync service and add our instance as an
358 // observer.
359 sync_service_ = profile->GetProfileSyncService();
360 sync_service_->AddObserver(this);
361 }
362
363 SetID(VIEW_ID_BOOKMARK_BAR);
364 Init();
365 SetProfile(profile);
366
367 size_animation_->Reset(IsAlwaysShown() ? 1 : 0);
368 }
369
~BookmarkBarView()370 BookmarkBarView::~BookmarkBarView() {
371 NotifyModelChanged();
372 if (model_)
373 model_->RemoveObserver(this);
374
375 // It's possible for the menu to outlive us, reset the observer to make sure
376 // it doesn't have a reference to us.
377 if (bookmark_menu_)
378 bookmark_menu_->set_observer(NULL);
379
380 StopShowFolderDropMenuTimer();
381
382 if (sync_service_)
383 sync_service_->RemoveObserver(this);
384 }
385
SetProfile(Profile * profile)386 void BookmarkBarView::SetProfile(Profile* profile) {
387 DCHECK(profile);
388 if (profile_ == profile)
389 return;
390
391 StopThrobbing(true);
392
393 // Cancels the current cancelable.
394 NotifyModelChanged();
395 registrar_.RemoveAll();
396
397 profile_ = profile;
398
399 if (model_)
400 model_->RemoveObserver(this);
401
402 // Disable the other bookmarked button, we'll re-enable when the model is
403 // loaded.
404 other_bookmarked_button_->SetEnabled(false);
405
406 Source<Profile> ns_source(profile_->GetOriginalProfile());
407 registrar_.Add(this, NotificationType::BOOKMARK_BUBBLE_SHOWN, ns_source);
408 registrar_.Add(this, NotificationType::BOOKMARK_BUBBLE_HIDDEN, ns_source);
409 registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
410 NotificationService::AllSources());
411
412 // Remove any existing bookmark buttons.
413 while (GetBookmarkButtonCount())
414 delete GetChildViewAt(0);
415
416 model_ = profile_->GetBookmarkModel();
417 if (model_) {
418 model_->AddObserver(this);
419 if (model_->IsLoaded())
420 Loaded(model_);
421 // else case: we'll receive notification back from the BookmarkModel when
422 // done loading, then we'll populate the bar.
423 }
424 }
425
SetPageNavigator(PageNavigator * navigator)426 void BookmarkBarView::SetPageNavigator(PageNavigator* navigator) {
427 page_navigator_ = navigator;
428 }
429
GetPreferredSize()430 gfx::Size BookmarkBarView::GetPreferredSize() {
431 return LayoutItems(true);
432 }
433
GetMinimumSize()434 gfx::Size BookmarkBarView::GetMinimumSize() {
435 // The minimum width of the bookmark bar should at least contain the overflow
436 // button, by which one can access all the Bookmark Bar items, and the "Other
437 // Bookmarks" folder, along with appropriate margins and button padding.
438 int width = kLeftMargin;
439
440 if (OnNewTabPage()) {
441 double current_state = 1 - size_animation_->GetCurrentValue();
442 width += 2 * static_cast<int>(kNewtabHorizontalPadding * current_state);
443 }
444
445 int sync_error_total_width = 0;
446 gfx::Size sync_error_button_pref = sync_error_button_->GetPreferredSize();
447 if (sync_ui_util::ShouldShowSyncErrorButton(sync_service_))
448 sync_error_total_width += kButtonPadding + sync_error_button_pref.width();
449
450 gfx::Size other_bookmarked_pref =
451 other_bookmarked_button_->GetPreferredSize();
452 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
453 gfx::Size bookmarks_separator_pref =
454 bookmarks_separator_view_->GetPreferredSize();
455
456 width += (other_bookmarked_pref.width() + kButtonPadding +
457 overflow_pref.width() + kButtonPadding +
458 bookmarks_separator_pref.width() + sync_error_total_width);
459
460 return gfx::Size(width, kBarHeight);
461 }
462
Layout()463 void BookmarkBarView::Layout() {
464 LayoutItems(false);
465 }
466
ViewHierarchyChanged(bool is_add,View * parent,View * child)467 void BookmarkBarView::ViewHierarchyChanged(bool is_add,
468 View* parent,
469 View* child) {
470 if (is_add && child == this) {
471 // We may get inserted into a hierarchy with a profile - this typically
472 // occurs when the bar's contents get populated fast enough that the
473 // buttons are created before the bar is attached to a frame.
474 UpdateColors();
475
476 if (height() > 0) {
477 // We only layout while parented. When we become parented, if our bounds
478 // haven't changed, OnBoundsChanged() won't get invoked and we won't
479 // layout. Therefore we always force a layout when added.
480 Layout();
481 }
482 }
483 }
484
PaintChildren(gfx::Canvas * canvas)485 void BookmarkBarView::PaintChildren(gfx::Canvas* canvas) {
486 View::PaintChildren(canvas);
487
488 if (drop_info_.get() && drop_info_->valid &&
489 drop_info_->drag_operation != 0 && drop_info_->drop_index != -1 &&
490 !drop_info_->is_over_overflow && !drop_info_->drop_on) {
491 int index = drop_info_->drop_index;
492 DCHECK(index <= GetBookmarkButtonCount());
493 int x = 0;
494 int y = 0;
495 int h = height();
496 if (index == GetBookmarkButtonCount()) {
497 if (index == 0) {
498 x = kLeftMargin;
499 } else {
500 x = GetBookmarkButton(index - 1)->x() +
501 GetBookmarkButton(index - 1)->width();
502 }
503 } else {
504 x = GetBookmarkButton(index)->x();
505 }
506 if (GetBookmarkButtonCount() > 0 && GetBookmarkButton(0)->IsVisible()) {
507 y = GetBookmarkButton(0)->y();
508 h = GetBookmarkButton(0)->height();
509 }
510
511 // Since the drop indicator is painted directly onto the canvas, we must
512 // make sure it is painted in the right location if the locale is RTL.
513 gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2,
514 y,
515 kDropIndicatorWidth,
516 h);
517 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
518
519 // TODO(sky/glen): make me pretty!
520 canvas->FillRectInt(kDropIndicatorColor, indicator_bounds.x(),
521 indicator_bounds.y(), indicator_bounds.width(),
522 indicator_bounds.height());
523 }
524 }
525
GetDropFormats(int * formats,std::set<ui::OSExchangeData::CustomFormat> * custom_formats)526 bool BookmarkBarView::GetDropFormats(
527 int* formats,
528 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
529 if (!model_ || !model_->IsLoaded())
530 return false;
531 *formats = ui::OSExchangeData::URL;
532 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat());
533 return true;
534 }
535
AreDropTypesRequired()536 bool BookmarkBarView::AreDropTypesRequired() {
537 return true;
538 }
539
CanDrop(const ui::OSExchangeData & data)540 bool BookmarkBarView::CanDrop(const ui::OSExchangeData& data) {
541 if (!model_ || !model_->IsLoaded() ||
542 !profile_->GetPrefs()->GetBoolean(prefs::kEditBookmarksEnabled))
543 return false;
544
545 if (!drop_info_.get())
546 drop_info_.reset(new DropInfo());
547
548 // Only accept drops of 1 node, which is the case for all data dragged from
549 // bookmark bar and menus.
550 return drop_info_->data.Read(data) && drop_info_->data.size() == 1;
551 }
552
OnDragEntered(const DropTargetEvent & event)553 void BookmarkBarView::OnDragEntered(const DropTargetEvent& event) {
554 }
555
OnDragUpdated(const DropTargetEvent & event)556 int BookmarkBarView::OnDragUpdated(const DropTargetEvent& event) {
557 if (!drop_info_.get())
558 return 0;
559
560 if (drop_info_->valid &&
561 (drop_info_->x == event.x() && drop_info_->y == event.y())) {
562 // The location of the mouse didn't change, return the last operation.
563 return drop_info_->drag_operation;
564 }
565
566 drop_info_->x = event.x();
567 drop_info_->y = event.y();
568
569 int drop_index;
570 bool drop_on;
571 bool is_over_overflow;
572 bool is_over_other;
573
574 drop_info_->drag_operation = CalculateDropOperation(
575 event, drop_info_->data, &drop_index, &drop_on, &is_over_overflow,
576 &is_over_other);
577
578 if (drop_info_->valid && drop_info_->drop_index == drop_index &&
579 drop_info_->drop_on == drop_on &&
580 drop_info_->is_over_overflow == is_over_overflow &&
581 drop_info_->is_over_other == is_over_other) {
582 // The position we're going to drop didn't change, return the last drag
583 // operation we calculated.
584 return drop_info_->drag_operation;
585 }
586
587 drop_info_->valid = true;
588
589 StopShowFolderDropMenuTimer();
590
591 // TODO(sky): Optimize paint region.
592 SchedulePaint();
593 drop_info_->drop_index = drop_index;
594 drop_info_->drop_on = drop_on;
595 drop_info_->is_over_overflow = is_over_overflow;
596 drop_info_->is_over_other = is_over_other;
597
598 if (drop_info_->is_menu_showing) {
599 if (bookmark_drop_menu_)
600 bookmark_drop_menu_->Cancel();
601 drop_info_->is_menu_showing = false;
602 }
603
604 if (drop_on || is_over_overflow || is_over_other) {
605 const BookmarkNode* node;
606 if (is_over_other)
607 node = model_->other_node();
608 else if (is_over_overflow)
609 node = model_->GetBookmarkBarNode();
610 else
611 node = model_->GetBookmarkBarNode()->GetChild(drop_index);
612 StartShowFolderDropMenuTimer(node);
613 }
614
615 return drop_info_->drag_operation;
616 }
617
OnDragExited()618 void BookmarkBarView::OnDragExited() {
619 StopShowFolderDropMenuTimer();
620
621 // NOTE: we don't hide the menu on exit as it's possible the user moved the
622 // mouse over the menu, which triggers an exit on us.
623
624 drop_info_->valid = false;
625
626 if (drop_info_->drop_index != -1) {
627 // TODO(sky): optimize the paint region.
628 SchedulePaint();
629 }
630 drop_info_.reset();
631 }
632
OnPerformDrop(const DropTargetEvent & event)633 int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
634 StopShowFolderDropMenuTimer();
635
636 if (bookmark_drop_menu_)
637 bookmark_drop_menu_->Cancel();
638
639 if (!drop_info_.get() || !drop_info_->drag_operation)
640 return ui::DragDropTypes::DRAG_NONE;
641
642 const BookmarkNode* root =
643 drop_info_->is_over_other ? model_->other_node() :
644 model_->GetBookmarkBarNode();
645 int index = drop_info_->drop_index;
646 const bool drop_on = drop_info_->drop_on;
647 const BookmarkNodeData data = drop_info_->data;
648 const bool is_over_other = drop_info_->is_over_other;
649 DCHECK(data.is_valid());
650
651 if (drop_info_->drop_index != -1) {
652 // TODO(sky): optimize the SchedulePaint region.
653 SchedulePaint();
654 }
655 drop_info_.reset();
656
657 const BookmarkNode* parent_node;
658 if (is_over_other) {
659 parent_node = root;
660 index = parent_node->child_count();
661 } else if (drop_on) {
662 parent_node = root->GetChild(index);
663 index = parent_node->child_count();
664 } else {
665 parent_node = root;
666 }
667 return bookmark_utils::PerformBookmarkDrop(profile_, data, parent_node,
668 index);
669 }
670
ShowContextMenu(const gfx::Point & p,bool is_mouse_gesture)671 void BookmarkBarView::ShowContextMenu(const gfx::Point& p,
672 bool is_mouse_gesture) {
673 ShowContextMenuForView(this, p, is_mouse_gesture);
674 }
675
GetAccessibleState(ui::AccessibleViewState * state)676 void BookmarkBarView::GetAccessibleState(ui::AccessibleViewState* state) {
677 state->role = ui::AccessibilityTypes::ROLE_TOOLBAR;
678 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS);
679 }
680
OnStateChanged()681 void BookmarkBarView::OnStateChanged() {
682 // When the sync state changes, it is sufficient to invoke View::Layout since
683 // during layout we query the profile sync service and determine whether the
684 // new state requires showing the sync error button so that the user can
685 // re-enter her password. If extension shelf appears along with the bookmark
686 // shelf, it too needs to be layed out. Since both have the same parent, it is
687 // enough to let the parent layout both of these children.
688 // TODO(sky): This should not require Layout() and SchedulePaint(). Needs
689 // some cleanup.
690 PreferredSizeChanged();
691 Layout();
692 SchedulePaint();
693 }
694
OnFullscreenToggled(bool fullscreen)695 void BookmarkBarView::OnFullscreenToggled(bool fullscreen) {
696 if (!fullscreen)
697 size_animation_->Reset(IsAlwaysShown() ? 1 : 0);
698 else if (IsAlwaysShown())
699 size_animation_->Reset(0);
700 }
701
IsDetached() const702 bool BookmarkBarView::IsDetached() const {
703 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNewTabPage4))
704 return false;
705
706 return OnNewTabPage() && (size_animation_->GetCurrentValue() != 1);
707 }
708
IsOnTop() const709 bool BookmarkBarView::IsOnTop() const {
710 return true;
711 }
712
GetAnimationValue() const713 double BookmarkBarView::GetAnimationValue() const {
714 return size_animation_->GetCurrentValue();
715 }
716
GetToolbarOverlap() const717 int BookmarkBarView::GetToolbarOverlap() const {
718 return GetToolbarOverlap(false);
719 }
720
IsAlwaysShown() const721 bool BookmarkBarView::IsAlwaysShown() const {
722 return (profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) &&
723 profile_->GetPrefs()->GetBoolean(prefs::kEnableBookmarkBar));
724 }
725
OnNewTabPage() const726 bool BookmarkBarView::OnNewTabPage() const {
727 return (browser_ && browser_->GetSelectedTabContents() &&
728 browser_->GetSelectedTabContents()->ShouldShowBookmarkBar());
729 }
730
GetToolbarOverlap(bool return_max) const731 int BookmarkBarView::GetToolbarOverlap(bool return_max) const {
732 // When not on the New Tab Page, always overlap by the full amount.
733 if (return_max || !OnNewTabPage())
734 return kToolbarOverlap;
735 // When on the New Tab Page with an infobar, overlap by 0 whenever the infobar
736 // is above us (i.e. when we're detached), since drawing over the infobar
737 // looks weird.
738 if (IsDetached() && infobar_visible_)
739 return 0;
740 // When on the New Tab Page with no infobar, animate the overlap between the
741 // attached and detached states.
742 return static_cast<int>(kToolbarOverlap * size_animation_->GetCurrentValue());
743 }
744
is_animating()745 bool BookmarkBarView::is_animating() {
746 return size_animation_->is_animating();
747 }
748
AnimationProgressed(const ui::Animation * animation)749 void BookmarkBarView::AnimationProgressed(const ui::Animation* animation) {
750 if (browser_)
751 browser_->BookmarkBarSizeChanged(true);
752 }
753
AnimationEnded(const ui::Animation * animation)754 void BookmarkBarView::AnimationEnded(const ui::Animation* animation) {
755 if (browser_)
756 browser_->BookmarkBarSizeChanged(false);
757
758 SchedulePaint();
759 }
760
BookmarkMenuDeleted(BookmarkMenuController * controller)761 void BookmarkBarView::BookmarkMenuDeleted(BookmarkMenuController* controller) {
762 if (controller == bookmark_menu_)
763 bookmark_menu_ = NULL;
764 else if (controller == bookmark_drop_menu_)
765 bookmark_drop_menu_ = NULL;
766 }
767
GetBookmarkButton(int index)768 views::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
769 DCHECK(index >= 0 && index < GetBookmarkButtonCount());
770 return static_cast<views::TextButton*>(GetChildViewAt(index));
771 }
772
GetMenu()773 views::MenuItemView* BookmarkBarView::GetMenu() {
774 return bookmark_menu_ ? bookmark_menu_->menu() : NULL;
775 }
776
GetContextMenu()777 views::MenuItemView* BookmarkBarView::GetContextMenu() {
778 return bookmark_menu_ ? bookmark_menu_->context_menu() : NULL;
779 }
780
GetDropMenu()781 views::MenuItemView* BookmarkBarView::GetDropMenu() {
782 return bookmark_drop_menu_ ? bookmark_drop_menu_->menu() : NULL;
783 }
784
GetNodeForButtonAt(const gfx::Point & loc,int * start_index)785 const BookmarkNode* BookmarkBarView::GetNodeForButtonAt(const gfx::Point& loc,
786 int* start_index) {
787 *start_index = 0;
788
789 if (loc.x() < 0 || loc.x() >= width() || loc.y() < 0 || loc.y() >= height())
790 return NULL;
791
792 gfx::Point adjusted_loc(GetMirroredXInView(loc.x()), loc.y());
793
794 // Check the buttons first.
795 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
796 views::View* child = GetChildViewAt(i);
797 if (!child->IsVisible())
798 break;
799 if (child->bounds().Contains(adjusted_loc))
800 return model_->GetBookmarkBarNode()->GetChild(i);
801 }
802
803 // Then the overflow button.
804 if (overflow_button_->IsVisible() &&
805 overflow_button_->bounds().Contains(adjusted_loc)) {
806 *start_index = GetFirstHiddenNodeIndex();
807 return model_->GetBookmarkBarNode();
808 }
809
810 // And finally the other folder.
811 if (other_bookmarked_button_->IsVisible() &&
812 other_bookmarked_button_->bounds().Contains(adjusted_loc)) {
813 return model_->other_node();
814 }
815
816 return NULL;
817 }
818
GetMenuButtonForNode(const BookmarkNode * node)819 views::MenuButton* BookmarkBarView::GetMenuButtonForNode(
820 const BookmarkNode* node) {
821 if (node == model_->other_node())
822 return other_bookmarked_button_;
823 if (node == model_->GetBookmarkBarNode())
824 return overflow_button_;
825 int index = model_->GetBookmarkBarNode()->GetIndexOf(node);
826 if (index == -1 || !node->is_folder())
827 return NULL;
828 return static_cast<views::MenuButton*>(GetChildViewAt(index));
829 }
830
GetAnchorPositionAndStartIndexForButton(views::MenuButton * button,MenuItemView::AnchorPosition * anchor,int * start_index)831 void BookmarkBarView::GetAnchorPositionAndStartIndexForButton(
832 views::MenuButton* button,
833 MenuItemView::AnchorPosition* anchor,
834 int* start_index) {
835 if (button == other_bookmarked_button_ || button == overflow_button_)
836 *anchor = MenuItemView::TOPRIGHT;
837 else
838 *anchor = MenuItemView::TOPLEFT;
839
840 // Invert orientation if right to left.
841 if (base::i18n::IsRTL()) {
842 if (*anchor == MenuItemView::TOPRIGHT)
843 *anchor = MenuItemView::TOPLEFT;
844 else
845 *anchor = MenuItemView::TOPRIGHT;
846 }
847
848 if (button == overflow_button_)
849 *start_index = GetFirstHiddenNodeIndex();
850 else
851 *start_index = 0;
852 }
853
ShowImportDialog()854 void BookmarkBarView::ShowImportDialog() {
855 browser_->OpenImportSettingsDialog();
856 }
857
Init()858 void BookmarkBarView::Init() {
859 // Note that at this point we're not in a hierarchy so GetThemeProvider() will
860 // return NULL. When we're inserted into a hierarchy, we'll call
861 // UpdateColors(), which will set the appropriate colors for all the objects
862 // added in this function.
863
864 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
865
866 if (!kDefaultFavicon)
867 kDefaultFavicon = rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
868
869 // Child views are traversed in the order they are added. Make sure the order
870 // they are added matches the visual order.
871 sync_error_button_ = CreateSyncErrorButton();
872 AddChildView(sync_error_button_);
873
874 overflow_button_ = CreateOverflowButton();
875 AddChildView(overflow_button_);
876
877 other_bookmarked_button_ = CreateOtherBookmarkedButton();
878 AddChildView(other_bookmarked_button_);
879
880 bookmarks_separator_view_ = new ButtonSeparatorView();
881 AddChildView(bookmarks_separator_view_);
882
883 instructions_ = new BookmarkBarInstructionsView(this);
884 AddChildView(instructions_);
885
886 SetContextMenuController(this);
887
888 size_animation_.reset(new ui::SlideAnimation(this));
889 }
890
CreateOtherBookmarkedButton()891 MenuButton* BookmarkBarView::CreateOtherBookmarkedButton() {
892 MenuButton* button = new BookmarkFolderButton(
893 this,
894 UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_BOOKMARKED)),
895 this,
896 false);
897 button->SetID(VIEW_ID_OTHER_BOOKMARKS);
898 button->SetIcon(GetFolderIcon());
899 button->SetContextMenuController(this);
900 button->set_tag(kOtherFolderButtonTag);
901 button->SetAccessibleName(
902 l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_BOOKMARKED));
903 return button;
904 }
905
CreateOverflowButton()906 MenuButton* BookmarkBarView::CreateOverflowButton() {
907 MenuButton* button = new OverFlowButton(this);
908 button->SetIcon(*ResourceBundle::GetSharedInstance().
909 GetBitmapNamed(IDR_BOOKMARK_BAR_CHEVRONS));
910
911 // The overflow button's image contains an arrow and therefore it is a
912 // direction sensitive image and we need to flip it if the UI layout is
913 // right-to-left.
914 //
915 // By default, menu buttons are not flipped because they generally contain
916 // text and flipping the gfx::Canvas object will break text rendering. Since
917 // the overflow button does not contain text, we can safely flip it.
918 button->EnableCanvasFlippingForRTLUI(true);
919
920 // Make visible as necessary.
921 button->SetVisible(false);
922 // Set accessibility name.
923 button->SetAccessibleName(
924 l10n_util::GetStringUTF16(IDS_ACCNAME_BOOKMARKS_CHEVRON));
925 return button;
926 }
927
Loaded(BookmarkModel * model)928 void BookmarkBarView::Loaded(BookmarkModel* model) {
929 volatile int button_count = GetBookmarkButtonCount();
930 DCHECK(button_count == 0); // If non-zero it means Load was invoked more than
931 // once, or we didn't properly clear things.
932 // Either of which shouldn't happen
933 const BookmarkNode* node = model_->GetBookmarkBarNode();
934 DCHECK(node && model_->other_node());
935 // Create a button for each of the children on the bookmark bar.
936 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
937 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
938 UpdateColors();
939 UpdateOtherBookmarksVisibility();
940 other_bookmarked_button_->SetEnabled(true);
941
942 Layout();
943 SchedulePaint();
944 }
945
BookmarkModelBeingDeleted(BookmarkModel * model)946 void BookmarkBarView::BookmarkModelBeingDeleted(BookmarkModel* model) {
947 // In normal shutdown The bookmark model should never be deleted before us.
948 // When X exits suddenly though, it can happen, This code exists
949 // to check for regressions in shutdown code and not crash.
950 if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers())
951 NOTREACHED();
952
953 // Do minimal cleanup, presumably we'll be deleted shortly.
954 NotifyModelChanged();
955 model_->RemoveObserver(this);
956 model_ = NULL;
957 }
958
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)959 void BookmarkBarView::BookmarkNodeMoved(BookmarkModel* model,
960 const BookmarkNode* old_parent,
961 int old_index,
962 const BookmarkNode* new_parent,
963 int new_index) {
964 bool was_throbbing = throbbing_view_ &&
965 throbbing_view_ == DetermineViewToThrobFromRemove(old_parent, old_index);
966 if (was_throbbing)
967 throbbing_view_->StopThrobbing();
968 BookmarkNodeRemovedImpl(model, old_parent, old_index);
969 BookmarkNodeAddedImpl(model, new_parent, new_index);
970 if (was_throbbing)
971 StartThrobbing(new_parent->GetChild(new_index), false);
972 }
973
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)974 void BookmarkBarView::BookmarkNodeAdded(BookmarkModel* model,
975 const BookmarkNode* parent,
976 int index) {
977 BookmarkNodeAddedImpl(model, parent, index);
978 }
979
BookmarkNodeAddedImpl(BookmarkModel * model,const BookmarkNode * parent,int index)980 void BookmarkBarView::BookmarkNodeAddedImpl(BookmarkModel* model,
981 const BookmarkNode* parent,
982 int index) {
983 UpdateOtherBookmarksVisibility();
984 NotifyModelChanged();
985 if (parent != model_->GetBookmarkBarNode()) {
986 // We only care about nodes on the bookmark bar.
987 return;
988 }
989 DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
990 const BookmarkNode* node = parent->GetChild(index);
991 if (!throbbing_view_ && sync_service_ && sync_service_->SetupInProgress()) {
992 StartThrobbing(node, true);
993 }
994 AddChildViewAt(CreateBookmarkButton(node), index);
995 UpdateColors();
996 Layout();
997 SchedulePaint();
998 }
999
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int old_index,const BookmarkNode * node)1000 void BookmarkBarView::BookmarkNodeRemoved(BookmarkModel* model,
1001 const BookmarkNode* parent,
1002 int old_index,
1003 const BookmarkNode* node) {
1004 BookmarkNodeRemovedImpl(model, parent, old_index);
1005 }
1006
BookmarkNodeRemovedImpl(BookmarkModel * model,const BookmarkNode * parent,int index)1007 void BookmarkBarView::BookmarkNodeRemovedImpl(BookmarkModel* model,
1008 const BookmarkNode* parent,
1009 int index) {
1010 UpdateOtherBookmarksVisibility();
1011
1012 StopThrobbing(true);
1013 // No need to start throbbing again as the bookmark bubble can't be up at
1014 // the same time as the user reorders.
1015
1016 NotifyModelChanged();
1017 if (parent != model_->GetBookmarkBarNode()) {
1018 // We only care about nodes on the bookmark bar.
1019 return;
1020 }
1021 DCHECK(index >= 0 && index < GetBookmarkButtonCount());
1022 views::View* button = GetChildViewAt(index);
1023 RemoveChildView(button);
1024 MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1025 Layout();
1026 SchedulePaint();
1027 }
1028
BookmarkNodeChanged(BookmarkModel * model,const BookmarkNode * node)1029 void BookmarkBarView::BookmarkNodeChanged(BookmarkModel* model,
1030 const BookmarkNode* node) {
1031 NotifyModelChanged();
1032 BookmarkNodeChangedImpl(model, node);
1033 }
1034
BookmarkNodeChangedImpl(BookmarkModel * model,const BookmarkNode * node)1035 void BookmarkBarView::BookmarkNodeChangedImpl(BookmarkModel* model,
1036 const BookmarkNode* node) {
1037 if (node->parent() != model_->GetBookmarkBarNode()) {
1038 // We only care about nodes on the bookmark bar.
1039 return;
1040 }
1041 int index = model_->GetBookmarkBarNode()->GetIndexOf(node);
1042 DCHECK_NE(-1, index);
1043 views::TextButton* button = GetBookmarkButton(index);
1044 gfx::Size old_pref = button->GetPreferredSize();
1045 ConfigureButton(node, button);
1046 gfx::Size new_pref = button->GetPreferredSize();
1047 if (old_pref.width() != new_pref.width()) {
1048 Layout();
1049 SchedulePaint();
1050 } else if (button->IsVisible()) {
1051 button->SchedulePaint();
1052 }
1053 }
1054
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)1055 void BookmarkBarView::BookmarkNodeChildrenReordered(BookmarkModel* model,
1056 const BookmarkNode* node) {
1057 NotifyModelChanged();
1058 if (node != model_->GetBookmarkBarNode())
1059 return; // We only care about reordering of the bookmark bar node.
1060
1061 // Remove the existing buttons.
1062 while (GetBookmarkButtonCount()) {
1063 views::View* button = GetChildViewAt(0);
1064 RemoveChildView(button);
1065 MessageLoop::current()->DeleteSoon(FROM_HERE, button);
1066 }
1067
1068 // Create the new buttons.
1069 for (int i = 0, child_count = node->child_count(); i < child_count; ++i)
1070 AddChildViewAt(CreateBookmarkButton(node->GetChild(i)), i);
1071 UpdateColors();
1072
1073 Layout();
1074 SchedulePaint();
1075 }
1076
BookmarkNodeFaviconLoaded(BookmarkModel * model,const BookmarkNode * node)1077 void BookmarkBarView::BookmarkNodeFaviconLoaded(BookmarkModel* model,
1078 const BookmarkNode* node) {
1079 BookmarkNodeChangedImpl(model, node);
1080 }
1081
WriteDragDataForView(View * sender,const gfx::Point & press_pt,ui::OSExchangeData * data)1082 void BookmarkBarView::WriteDragDataForView(View* sender,
1083 const gfx::Point& press_pt,
1084 ui::OSExchangeData* data) {
1085 UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_DragButton"),
1086 profile_);
1087
1088 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1089 if (sender == GetBookmarkButton(i)) {
1090 views::TextButton* button = GetBookmarkButton(i);
1091 gfx::CanvasSkia canvas(button->width(), button->height(), false);
1092 button->PaintButton(&canvas, views::TextButton::PB_FOR_DRAG);
1093 drag_utils::SetDragImageOnDataObject(canvas, button->size(), press_pt,
1094 data);
1095 WriteBookmarkDragData(model_->GetBookmarkBarNode()->GetChild(i), data);
1096 return;
1097 }
1098 }
1099 NOTREACHED();
1100 }
1101
GetDragOperationsForView(View * sender,const gfx::Point & p)1102 int BookmarkBarView::GetDragOperationsForView(View* sender,
1103 const gfx::Point& p) {
1104 if (size_animation_->is_animating() ||
1105 (size_animation_->GetCurrentValue() == 0 && !OnNewTabPage())) {
1106 // Don't let the user drag while animating open or we're closed (and not on
1107 // the new tab page, on the new tab page size_animation_ is always 0). This
1108 // typically is only hit if the user does something to inadvertanty trigger
1109 // dnd, such as pressing the mouse and hitting control-b.
1110 return ui::DragDropTypes::DRAG_NONE;
1111 }
1112
1113 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1114 if (sender == GetBookmarkButton(i)) {
1115 return bookmark_utils::BookmarkDragOperation(
1116 profile_, model_->GetBookmarkBarNode()->GetChild(i));
1117 }
1118 }
1119 NOTREACHED();
1120 return ui::DragDropTypes::DRAG_NONE;
1121 }
1122
CanStartDragForView(views::View * sender,const gfx::Point & press_pt,const gfx::Point & p)1123 bool BookmarkBarView::CanStartDragForView(views::View* sender,
1124 const gfx::Point& press_pt,
1125 const gfx::Point& p) {
1126 // Check if we have not moved enough horizontally but we have moved downward
1127 // vertically - downward drag.
1128 if (!View::ExceededDragThreshold(press_pt.x() - p.x(), 0) &&
1129 press_pt.y() < p.y()) {
1130 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1131 if (sender == GetBookmarkButton(i)) {
1132 const BookmarkNode* node = model_->GetBookmarkBarNode()->GetChild(i);
1133 // If the folder button was dragged, show the menu instead.
1134 if (node && node->is_folder()) {
1135 views::MenuButton* menu_button =
1136 static_cast<views::MenuButton*>(sender);
1137 menu_button->Activate();
1138 return false;
1139 }
1140 break;
1141 }
1142 }
1143 }
1144 return true;
1145 }
1146
WriteBookmarkDragData(const BookmarkNode * node,ui::OSExchangeData * data)1147 void BookmarkBarView::WriteBookmarkDragData(const BookmarkNode* node,
1148 ui::OSExchangeData* data) {
1149 DCHECK(node && data);
1150 BookmarkNodeData drag_data(node);
1151 drag_data.Write(profile_, data);
1152 }
1153
RunMenu(views::View * view,const gfx::Point & pt)1154 void BookmarkBarView::RunMenu(views::View* view, const gfx::Point& pt) {
1155 const BookmarkNode* node;
1156
1157 int start_index = 0;
1158 if (view == other_bookmarked_button_) {
1159 node = model_->other_node();
1160 } else if (view == overflow_button_) {
1161 node = model_->GetBookmarkBarNode();
1162 start_index = GetFirstHiddenNodeIndex();
1163 } else {
1164 int button_index = GetIndexOf(view);
1165 DCHECK_NE(-1, button_index);
1166 node = model_->GetBookmarkBarNode()->GetChild(button_index);
1167 }
1168
1169 bookmark_menu_ = new BookmarkMenuController(browser_, profile_,
1170 page_navigator_, GetWindow()->GetNativeWindow(), node, start_index);
1171 bookmark_menu_->set_observer(this);
1172 bookmark_menu_->RunMenuAt(this, false);
1173 }
1174
ButtonPressed(views::Button * sender,const views::Event & event)1175 void BookmarkBarView::ButtonPressed(views::Button* sender,
1176 const views::Event& event) {
1177 // Show the login wizard if the user clicked the re-login button.
1178 if (sender->tag() == kSyncErrorButtonTag) {
1179 DCHECK(sender == sync_error_button_);
1180 DCHECK(sync_service_ && !sync_service_->IsManaged());
1181 sync_service_->ShowErrorUI(GetWindow()->GetNativeWindow());
1182 return;
1183 }
1184
1185 const BookmarkNode* node;
1186 if (sender->tag() == kOtherFolderButtonTag) {
1187 node = model_->other_node();
1188 } else {
1189 int index = GetIndexOf(sender);
1190 DCHECK_NE(-1, index);
1191 node = model_->GetBookmarkBarNode()->GetChild(index);
1192 }
1193 DCHECK(page_navigator_);
1194
1195 WindowOpenDisposition disposition_from_event_flags =
1196 event_utils::DispositionFromEventFlags(sender->mouse_event_flags());
1197
1198 if (node->is_url()) {
1199 RecordAppLaunch(profile_, node->GetURL());
1200 page_navigator_->OpenURL(node->GetURL(), GURL(),
1201 disposition_from_event_flags, PageTransition::AUTO_BOOKMARK);
1202 } else {
1203 bookmark_utils::OpenAll(GetWindow()->GetNativeWindow(), profile_,
1204 GetPageNavigator(), node, disposition_from_event_flags);
1205 }
1206 UserMetrics::RecordAction(UserMetricsAction("ClickedBookmarkBarURLButton"),
1207 profile_);
1208 }
1209
ShowContextMenuForView(View * source,const gfx::Point & p,bool is_mouse_gesture)1210 void BookmarkBarView::ShowContextMenuForView(View* source,
1211 const gfx::Point& p,
1212 bool is_mouse_gesture) {
1213 if (!model_->IsLoaded()) {
1214 // Don't do anything if the model isn't loaded.
1215 return;
1216 }
1217
1218 const BookmarkNode* parent = NULL;
1219 std::vector<const BookmarkNode*> nodes;
1220 if (source == other_bookmarked_button_) {
1221 parent = model_->other_node();
1222 // Do this so the user can open all bookmarks. BookmarkContextMenu makes
1223 // sure the user can edit/delete the node in this case.
1224 nodes.push_back(parent);
1225 } else if (source != this) {
1226 // User clicked on one of the bookmark buttons, find which one they
1227 // clicked on.
1228 int bookmark_button_index = GetIndexOf(source);
1229 DCHECK(bookmark_button_index != -1 &&
1230 bookmark_button_index < GetBookmarkButtonCount());
1231 const BookmarkNode* node =
1232 model_->GetBookmarkBarNode()->GetChild(bookmark_button_index);
1233 nodes.push_back(node);
1234 parent = node->parent();
1235 } else {
1236 parent = model_->GetBookmarkBarNode();
1237 nodes.push_back(parent);
1238 }
1239 // Browser may be null during testing.
1240 PageNavigator* navigator =
1241 browser() ? browser()->GetSelectedTabContents() : NULL;
1242 BookmarkContextMenu controller(GetWindow()->GetNativeWindow(), GetProfile(),
1243 navigator, parent, nodes);
1244 controller.RunMenuAt(p);
1245 }
1246
CreateBookmarkButton(const BookmarkNode * node)1247 views::View* BookmarkBarView::CreateBookmarkButton(const BookmarkNode* node) {
1248 if (node->is_url()) {
1249 BookmarkButton* button = new BookmarkButton(this, node->GetURL(),
1250 UTF16ToWide(node->GetTitle()), GetProfile());
1251 ConfigureButton(node, button);
1252 return button;
1253 } else {
1254 views::MenuButton* button = new BookmarkFolderButton(this,
1255 UTF16ToWide(node->GetTitle()), this, false);
1256 button->SetIcon(GetFolderIcon());
1257 ConfigureButton(node, button);
1258 return button;
1259 }
1260 }
1261
ConfigureButton(const BookmarkNode * node,views::TextButton * button)1262 void BookmarkBarView::ConfigureButton(const BookmarkNode* node,
1263 views::TextButton* button) {
1264 button->SetText(UTF16ToWide(node->GetTitle()));
1265 button->SetAccessibleName(node->GetTitle());
1266 button->SetID(VIEW_ID_BOOKMARK_BAR_ELEMENT);
1267 // We don't always have a theme provider (ui tests, for example).
1268 if (GetThemeProvider()) {
1269 button->SetEnabledColor(GetThemeProvider()->GetColor(
1270 ThemeService::COLOR_BOOKMARK_TEXT));
1271 }
1272
1273 button->ClearMaxTextSize();
1274 button->SetContextMenuController(this);
1275 button->SetDragController(this);
1276 if (node->is_url()) {
1277 if (model_->GetFavicon(node).width() != 0)
1278 button->SetIcon(model_->GetFavicon(node));
1279 else
1280 button->SetIcon(*kDefaultFavicon);
1281 }
1282 button->set_max_width(kMaxButtonWidth);
1283 }
1284
IsItemChecked(int id) const1285 bool BookmarkBarView::IsItemChecked(int id) const {
1286 DCHECK(id == kAlwaysShowCommandID);
1287 return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
1288 }
1289
ExecuteCommand(int id)1290 void BookmarkBarView::ExecuteCommand(int id) {
1291 bookmark_utils::ToggleWhenVisible(profile_);
1292 }
1293
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)1294 void BookmarkBarView::Observe(NotificationType type,
1295 const NotificationSource& source,
1296 const NotificationDetails& details) {
1297 DCHECK(profile_);
1298 switch (type.value) {
1299 case NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED:
1300 if (IsAlwaysShown()) {
1301 size_animation_->Show();
1302 } else {
1303 size_animation_->Hide();
1304 }
1305 break;
1306
1307 case NotificationType::BOOKMARK_BUBBLE_SHOWN: {
1308 StopThrobbing(true);
1309 GURL url = *(Details<GURL>(details).ptr());
1310 const BookmarkNode* node = model_->GetMostRecentlyAddedNodeForURL(url);
1311 if (!node)
1312 return; // Generally shouldn't happen.
1313 StartThrobbing(node, false);
1314 break;
1315 }
1316 case NotificationType::BOOKMARK_BUBBLE_HIDDEN:
1317 StopThrobbing(false);
1318 break;
1319
1320 default:
1321 NOTREACHED();
1322 break;
1323 }
1324 }
1325
OnThemeChanged()1326 void BookmarkBarView::OnThemeChanged() {
1327 UpdateColors();
1328 }
1329
NotifyModelChanged()1330 void BookmarkBarView::NotifyModelChanged() {
1331 if (model_changed_listener_)
1332 model_changed_listener_->ModelChanged();
1333 }
1334
ShowDropFolderForNode(const BookmarkNode * node)1335 void BookmarkBarView::ShowDropFolderForNode(const BookmarkNode* node) {
1336 if (bookmark_drop_menu_) {
1337 if (bookmark_drop_menu_->node() == node) {
1338 // Already showing for the specified node.
1339 return;
1340 }
1341 bookmark_drop_menu_->Cancel();
1342 }
1343
1344 views::MenuButton* menu_button = GetMenuButtonForNode(node);
1345 if (!menu_button)
1346 return;
1347
1348 int start_index = 0;
1349 if (node == model_->GetBookmarkBarNode())
1350 start_index = GetFirstHiddenNodeIndex();
1351
1352 drop_info_->is_menu_showing = true;
1353 bookmark_drop_menu_ = new BookmarkMenuController(browser_, profile_,
1354 page_navigator_, GetWindow()->GetNativeWindow(), node, start_index);
1355 bookmark_drop_menu_->set_observer(this);
1356 bookmark_drop_menu_->RunMenuAt(this, true);
1357 }
1358
StopShowFolderDropMenuTimer()1359 void BookmarkBarView::StopShowFolderDropMenuTimer() {
1360 if (show_folder_drop_menu_task_)
1361 show_folder_drop_menu_task_->Cancel();
1362 }
1363
StartShowFolderDropMenuTimer(const BookmarkNode * node)1364 void BookmarkBarView::StartShowFolderDropMenuTimer(const BookmarkNode* node) {
1365 if (testing_) {
1366 // So that tests can run as fast as possible disable the delay during
1367 // testing.
1368 ShowDropFolderForNode(node);
1369 return;
1370 }
1371 DCHECK(!show_folder_drop_menu_task_);
1372 show_folder_drop_menu_task_ = new ShowFolderDropMenuTask(this, node);
1373 int delay = views::GetMenuShowDelay();
1374 MessageLoop::current()->PostDelayedTask(FROM_HERE,
1375 show_folder_drop_menu_task_, delay);
1376 }
1377
CalculateDropOperation(const DropTargetEvent & event,const BookmarkNodeData & data,int * index,bool * drop_on,bool * is_over_overflow,bool * is_over_other)1378 int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event,
1379 const BookmarkNodeData& data,
1380 int* index,
1381 bool* drop_on,
1382 bool* is_over_overflow,
1383 bool* is_over_other) {
1384 DCHECK(model_);
1385 DCHECK(model_->IsLoaded());
1386 DCHECK(data.is_valid());
1387
1388 // The drop event uses the screen coordinates while the child Views are
1389 // always laid out from left to right (even though they are rendered from
1390 // right-to-left on RTL locales). Thus, in order to make sure the drop
1391 // coordinates calculation works, we mirror the event's X coordinate if the
1392 // locale is RTL.
1393 int mirrored_x = GetMirroredXInView(event.x());
1394
1395 *index = -1;
1396 *drop_on = false;
1397 *is_over_other = *is_over_overflow = false;
1398
1399 bool found = false;
1400 const int other_delta_x = mirrored_x - other_bookmarked_button_->x();
1401 if (other_bookmarked_button_->IsVisible() && other_delta_x >= 0 &&
1402 other_delta_x < other_bookmarked_button_->width()) {
1403 // Mouse is over 'other' folder.
1404 *is_over_other = true;
1405 *drop_on = true;
1406 found = true;
1407 } else if (!GetBookmarkButtonCount()) {
1408 // No bookmarks, accept the drop.
1409 *index = 0;
1410 int ops = data.GetFirstNode(profile_)
1411 ? ui::DragDropTypes::DRAG_MOVE
1412 : ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
1413 return bookmark_utils::PreferredDropOperation(event.source_operations(),
1414 ops);
1415 }
1416
1417 for (int i = 0; i < GetBookmarkButtonCount() &&
1418 GetBookmarkButton(i)->IsVisible() && !found; i++) {
1419 views::TextButton* button = GetBookmarkButton(i);
1420 int button_x = mirrored_x - button->x();
1421 int button_w = button->width();
1422 if (button_x < button_w) {
1423 found = true;
1424 const BookmarkNode* node = model_->GetBookmarkBarNode()->GetChild(i);
1425 if (node->is_folder()) {
1426 if (button_x <= views::kDropBetweenPixels) {
1427 *index = i;
1428 } else if (button_x < button_w - views::kDropBetweenPixels) {
1429 *index = i;
1430 *drop_on = true;
1431 } else {
1432 *index = i + 1;
1433 }
1434 } else if (button_x < button_w / 2) {
1435 *index = i;
1436 } else {
1437 *index = i + 1;
1438 }
1439 break;
1440 }
1441 }
1442
1443 if (!found) {
1444 if (overflow_button_->IsVisible()) {
1445 // Are we over the overflow button?
1446 int overflow_delta_x = mirrored_x - overflow_button_->x();
1447 if (overflow_delta_x >= 0 &&
1448 overflow_delta_x < overflow_button_->width()) {
1449 // Mouse is over overflow button.
1450 *index = GetFirstHiddenNodeIndex();
1451 *is_over_overflow = true;
1452 } else if (overflow_delta_x < 0) {
1453 // Mouse is after the last visible button but before overflow button;
1454 // use the last visible index.
1455 *index = GetFirstHiddenNodeIndex();
1456 } else {
1457 return ui::DragDropTypes::DRAG_NONE;
1458 }
1459 } else if (!other_bookmarked_button_->IsVisible() ||
1460 mirrored_x < other_bookmarked_button_->x()) {
1461 // Mouse is after the last visible button but before more recently
1462 // bookmarked; use the last visible index.
1463 *index = GetFirstHiddenNodeIndex();
1464 } else {
1465 return ui::DragDropTypes::DRAG_NONE;
1466 }
1467 }
1468
1469 if (*drop_on) {
1470 const BookmarkNode* parent =
1471 *is_over_other ? model_->other_node() :
1472 model_->GetBookmarkBarNode()->GetChild(*index);
1473 int operation =
1474 bookmark_utils::BookmarkDropOperation(profile_, event, data, parent,
1475 parent->child_count());
1476 if (!operation && !data.has_single_url() &&
1477 data.GetFirstNode(profile_) == parent) {
1478 // Don't open a menu if the node being dragged is the the menu to
1479 // open.
1480 *drop_on = false;
1481 }
1482 return operation;
1483 }
1484 return bookmark_utils::BookmarkDropOperation(profile_, event, data,
1485 model_->GetBookmarkBarNode(),
1486 *index);
1487 }
1488
GetFirstHiddenNodeIndex()1489 int BookmarkBarView::GetFirstHiddenNodeIndex() {
1490 const int bb_count = GetBookmarkButtonCount();
1491 for (int i = 0; i < bb_count; ++i) {
1492 if (!GetBookmarkButton(i)->IsVisible())
1493 return i;
1494 }
1495 return bb_count;
1496 }
1497
StartThrobbing(const BookmarkNode * node,bool overflow_only)1498 void BookmarkBarView::StartThrobbing(const BookmarkNode* node,
1499 bool overflow_only) {
1500 DCHECK(!throbbing_view_);
1501
1502 // Determine which visible button is showing the bookmark (or is an ancestor
1503 // of the bookmark).
1504 const BookmarkNode* bbn = model_->GetBookmarkBarNode();
1505 const BookmarkNode* parent_on_bb = node;
1506 while (parent_on_bb) {
1507 const BookmarkNode* parent = parent_on_bb->parent();
1508 if (parent == bbn)
1509 break;
1510 parent_on_bb = parent;
1511 }
1512 if (parent_on_bb) {
1513 int index = bbn->GetIndexOf(parent_on_bb);
1514 if (index >= GetFirstHiddenNodeIndex()) {
1515 // Node is hidden, animate the overflow button.
1516 throbbing_view_ = overflow_button_;
1517 } else if (!overflow_only) {
1518 throbbing_view_ = static_cast<CustomButton*>(GetChildViewAt(index));
1519 }
1520 } else if (!overflow_only) {
1521 throbbing_view_ = other_bookmarked_button_;
1522 }
1523
1524 // Use a large number so that the button continues to throb.
1525 if (throbbing_view_)
1526 throbbing_view_->StartThrobbing(std::numeric_limits<int>::max());
1527 }
1528
DetermineViewToThrobFromRemove(const BookmarkNode * parent,int old_index)1529 views::CustomButton* BookmarkBarView::DetermineViewToThrobFromRemove(
1530 const BookmarkNode* parent,
1531 int old_index) {
1532 const BookmarkNode* bbn = model_->GetBookmarkBarNode();
1533 const BookmarkNode* old_node = parent;
1534 int old_index_on_bb = old_index;
1535 while (old_node && old_node != bbn) {
1536 const BookmarkNode* parent = old_node->parent();
1537 if (parent == bbn) {
1538 old_index_on_bb = bbn->GetIndexOf(old_node);
1539 break;
1540 }
1541 old_node = parent;
1542 }
1543 if (old_node) {
1544 if (old_index_on_bb >= GetFirstHiddenNodeIndex()) {
1545 // Node is hidden, animate the overflow button.
1546 return overflow_button_;
1547 }
1548 return static_cast<CustomButton*>(GetChildViewAt(old_index_on_bb));
1549 }
1550 // Node wasn't on the bookmark bar, use the other bookmark button.
1551 return other_bookmarked_button_;
1552 }
1553
GetBookmarkButtonCount()1554 int BookmarkBarView::GetBookmarkButtonCount() {
1555 // We contain five non-bookmark button views: other bookmarks, bookmarks
1556 // separator, chevrons (for overflow), the instruction label and the sync
1557 // error button.
1558 return child_count() - 5;
1559 }
1560
StopThrobbing(bool immediate)1561 void BookmarkBarView::StopThrobbing(bool immediate) {
1562 if (!throbbing_view_)
1563 return;
1564
1565 // If not immediate, cycle through 2 more complete cycles.
1566 throbbing_view_->StartThrobbing(immediate ? 0 : 4);
1567 throbbing_view_ = NULL;
1568 }
1569
1570 // static
CreateToolTipForURLAndTitle(const gfx::Point & screen_loc,const GURL & url,const std::wstring & title,Profile * profile)1571 std::wstring BookmarkBarView::CreateToolTipForURLAndTitle(
1572 const gfx::Point& screen_loc,
1573 const GURL& url,
1574 const std::wstring& title,
1575 Profile* profile) {
1576 int max_width = views::TooltipManager::GetMaxWidth(screen_loc.x(),
1577 screen_loc.y());
1578 gfx::Font tt_font = views::TooltipManager::GetDefaultFont();
1579 std::wstring result;
1580
1581 // First the title.
1582 if (!title.empty()) {
1583 std::wstring localized_title = title;
1584 base::i18n::AdjustStringForLocaleDirection(&localized_title);
1585 result.append(UTF16ToWideHack(ui::ElideText(WideToUTF16Hack(
1586 localized_title), tt_font, max_width, false)));
1587 }
1588
1589 // Only show the URL if the url and title differ.
1590 if (title != UTF8ToWide(url.spec())) {
1591 if (!result.empty())
1592 result.append(views::TooltipManager::GetLineSeparator());
1593
1594 // We need to explicitly specify the directionality of the URL's text to
1595 // make sure it is treated as an LTR string when the context is RTL. For
1596 // example, the URL "http://www.yahoo.com/" appears as
1597 // "/http://www.yahoo.com" when rendered, as is, in an RTL context since
1598 // the Unicode BiDi algorithm puts certain characters on the left by
1599 // default.
1600 std::string languages = profile->GetPrefs()->GetString(
1601 prefs::kAcceptLanguages);
1602 string16 elided_url(ui::ElideUrl(url, tt_font, max_width, languages));
1603 elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
1604 result.append(UTF16ToWideHack(elided_url));
1605 }
1606 return result;
1607 }
1608
UpdateColors()1609 void BookmarkBarView::UpdateColors() {
1610 // We don't always have a theme provider (ui tests, for example).
1611 const ui::ThemeProvider* theme_provider = GetThemeProvider();
1612 if (!theme_provider)
1613 return;
1614 SkColor text_color =
1615 theme_provider->GetColor(ThemeService::COLOR_BOOKMARK_TEXT);
1616 for (int i = 0; i < GetBookmarkButtonCount(); ++i)
1617 GetBookmarkButton(i)->SetEnabledColor(text_color);
1618 other_bookmarked_button()->SetEnabledColor(text_color);
1619 }
1620
UpdateOtherBookmarksVisibility()1621 void BookmarkBarView::UpdateOtherBookmarksVisibility() {
1622 bool has_other_children = model_->other_node()->child_count() > 0;
1623 if (has_other_children == other_bookmarked_button_->IsVisible())
1624 return;
1625 other_bookmarked_button_->SetVisible(has_other_children);
1626 bookmarks_separator_view_->SetVisible(has_other_children);
1627 Layout();
1628 SchedulePaint();
1629 }
1630
LayoutItems(bool compute_bounds_only)1631 gfx::Size BookmarkBarView::LayoutItems(bool compute_bounds_only) {
1632 gfx::Size prefsize;
1633 if (!parent() && !compute_bounds_only)
1634 return prefsize;
1635
1636 int x = kLeftMargin;
1637 int top_margin = IsDetached() ? kDetachedTopMargin : 0;
1638 int y = top_margin;
1639 int width = View::width() - kRightMargin - kLeftMargin;
1640 int height = -top_margin - kBottomMargin;
1641 int separator_margin = kSeparatorMargin;
1642
1643 if (OnNewTabPage()) {
1644 double current_state = 1 - size_animation_->GetCurrentValue();
1645 x += static_cast<int>(kNewtabHorizontalPadding * current_state);
1646 y += static_cast<int>(kNewtabVerticalPadding * current_state);
1647 width -= static_cast<int>(kNewtabHorizontalPadding * current_state);
1648 height += View::height() -
1649 static_cast<int>(kNewtabVerticalPadding * 2 * current_state);
1650 separator_margin -= static_cast<int>(kSeparatorMargin * current_state);
1651 } else {
1652 // For the attached appearance, pin the content to the bottom of the bar
1653 // when animating in/out, as shrinking its height instead looks weird. This
1654 // also matches how we layout infobars.
1655 y += View::height() - kBarHeight;
1656 height += kBarHeight;
1657 }
1658
1659 gfx::Size other_bookmarked_pref =
1660 other_bookmarked_button_->IsVisible() ?
1661 other_bookmarked_button_->GetPreferredSize() : gfx::Size();
1662 gfx::Size overflow_pref = overflow_button_->GetPreferredSize();
1663 gfx::Size bookmarks_separator_pref =
1664 bookmarks_separator_view_->GetPreferredSize();
1665
1666 int sync_error_total_width = 0;
1667 gfx::Size sync_error_button_pref = sync_error_button_->GetPreferredSize();
1668 if (sync_ui_util::ShouldShowSyncErrorButton(sync_service_)) {
1669 sync_error_total_width += kButtonPadding + sync_error_button_pref.width();
1670 }
1671 int max_x = width - overflow_pref.width() - kButtonPadding -
1672 bookmarks_separator_pref.width() - sync_error_total_width;
1673 if (other_bookmarked_button_->IsVisible())
1674 max_x -= other_bookmarked_pref.width() + kButtonPadding;
1675
1676 // Next, layout out the buttons. Any buttons that are placed beyond the
1677 // visible region and made invisible.
1678 if (GetBookmarkButtonCount() == 0 && model_ && model_->IsLoaded()) {
1679 gfx::Size pref = instructions_->GetPreferredSize();
1680 if (!compute_bounds_only) {
1681 instructions_->SetBounds(
1682 x + kInstructionsPadding, y,
1683 std::min(static_cast<int>(pref.width()),
1684 max_x - x),
1685 height);
1686 instructions_->SetVisible(true);
1687 }
1688 } else {
1689 if (!compute_bounds_only)
1690 instructions_->SetVisible(false);
1691
1692 for (int i = 0; i < GetBookmarkButtonCount(); ++i) {
1693 views::View* child = GetChildViewAt(i);
1694 gfx::Size pref = child->GetPreferredSize();
1695 int next_x = x + pref.width() + kButtonPadding;
1696 if (!compute_bounds_only) {
1697 child->SetVisible(next_x < max_x);
1698 child->SetBounds(x, y, pref.width(), height);
1699 }
1700 x = next_x;
1701 }
1702 }
1703
1704 // Layout the right side of the bar.
1705 const bool all_visible =
1706 (GetBookmarkButtonCount() == 0 ||
1707 GetChildViewAt(GetBookmarkButtonCount() - 1)->IsVisible());
1708
1709 // Layout the right side buttons.
1710 if (!compute_bounds_only)
1711 x = max_x + kButtonPadding;
1712 else
1713 x += kButtonPadding;
1714
1715 // The overflow button.
1716 if (!compute_bounds_only) {
1717 overflow_button_->SetBounds(x, y, overflow_pref.width(), height);
1718 overflow_button_->SetVisible(!all_visible);
1719 }
1720 x += overflow_pref.width();
1721
1722 // Separator.
1723 if (bookmarks_separator_view_->IsVisible()) {
1724 if (!compute_bounds_only) {
1725 bookmarks_separator_view_->SetBounds(x,
1726 y - top_margin,
1727 bookmarks_separator_pref.width(),
1728 height + top_margin + kBottomMargin -
1729 separator_margin);
1730 }
1731
1732 x += bookmarks_separator_pref.width();
1733 }
1734
1735 // The other bookmarks button.
1736 if (other_bookmarked_button_->IsVisible()) {
1737 if (!compute_bounds_only) {
1738 other_bookmarked_button_->SetBounds(x, y, other_bookmarked_pref.width(),
1739 height);
1740 }
1741 x += other_bookmarked_pref.width() + kButtonPadding;
1742 }
1743
1744 // Set the real bounds of the sync error button only if it needs to appear on
1745 // the bookmarks bar.
1746 if (sync_ui_util::ShouldShowSyncErrorButton(sync_service_)) {
1747 x += kButtonPadding;
1748 if (!compute_bounds_only) {
1749 sync_error_button_->SetBounds(
1750 x, y, sync_error_button_pref.width(), height);
1751 sync_error_button_->SetVisible(true);
1752 }
1753 x += sync_error_button_pref.width();
1754 } else if (!compute_bounds_only) {
1755 sync_error_button_->SetBounds(x, y, 0, height);
1756 sync_error_button_->SetVisible(false);
1757 }
1758
1759 // Set the preferred size computed so far.
1760 if (compute_bounds_only) {
1761 x += kRightMargin;
1762 prefsize.set_width(x);
1763 if (OnNewTabPage()) {
1764 x += static_cast<int>(
1765 kNewtabHorizontalPadding * (1 - size_animation_->GetCurrentValue()));
1766 prefsize.set_height(kBarHeight +
1767 static_cast<int>((kNewtabBarHeight - kBarHeight) *
1768 (1 - size_animation_->GetCurrentValue())));
1769 } else {
1770 prefsize.set_height(
1771 static_cast<int>(kBarHeight * size_animation_->GetCurrentValue()));
1772 }
1773 }
1774 return prefsize;
1775 }
1776
CreateSyncErrorButton()1777 views::TextButton* BookmarkBarView::CreateSyncErrorButton() {
1778 views::TextButton* sync_error_button =
1779 new views::TextButton(this, UTF16ToWide(
1780 l10n_util::GetStringUTF16(IDS_SYNC_BOOKMARK_BAR_ERROR)));
1781 sync_error_button->set_tag(kSyncErrorButtonTag);
1782
1783 // The tooltip is the only way we have to display text explaining the error
1784 // to the user.
1785 sync_error_button->SetTooltipText(
1786 UTF16ToWide(l10n_util::GetStringUTF16(IDS_SYNC_BOOKMARK_BAR_ERROR_DESC)));
1787 sync_error_button->SetAccessibleName(
1788 l10n_util::GetStringUTF16(IDS_ACCNAME_SYNC_ERROR_BUTTON));
1789 sync_error_button->SetIcon(
1790 *ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING));
1791 return sync_error_button;
1792 }
1793