[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bubble_view.cc
blobb126ced9eb0752d5067d6568e39d1d4d5084c471
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_bubble_view.h"
7 #include "base/strings/string16.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
14 #include "chrome/browser/ui/sync/sync_promo_ui.h"
15 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view_observer.h"
16 #include "chrome/browser/ui/views/bookmarks/bookmark_sync_promo_view.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "components/bookmarks/browser/bookmark_utils.h"
19 #include "content/public/browser/user_metrics.h"
20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h"
22 #include "ui/accessibility/ax_view_state.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/events/keycodes/keyboard_codes.h"
26 #include "ui/views/bubble/bubble_frame_view.h"
27 #include "ui/views/controls/button/label_button.h"
28 #include "ui/views/controls/combobox/combobox.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/controls/link.h"
31 #include "ui/views/controls/textfield/textfield.h"
32 #include "ui/views/layout/grid_layout.h"
33 #include "ui/views/layout/layout_constants.h"
34 #include "ui/views/widget/widget.h"
36 using base::UserMetricsAction;
37 using views::ColumnSet;
38 using views::GridLayout;
40 namespace {
42 // Minimum width of the the bubble.
43 const int kMinBubbleWidth = 350;
45 // Width of the border of a button.
46 const int kControlBorderWidth = 2;
48 } // namespace
50 BookmarkBubbleView* BookmarkBubbleView::bookmark_bubble_ = NULL;
52 // static
53 void BookmarkBubbleView::ShowBubble(views::View* anchor_view,
54 BookmarkBubbleViewObserver* observer,
55 scoped_ptr<BookmarkBubbleDelegate> delegate,
56 Profile* profile,
57 const GURL& url,
58 bool newly_bookmarked) {
59 if (IsShowing())
60 return;
62 bookmark_bubble_ = new BookmarkBubbleView(anchor_view,
63 observer,
64 delegate.Pass(),
65 profile,
66 url,
67 newly_bookmarked);
68 views::BubbleDelegateView::CreateBubble(bookmark_bubble_)->Show();
69 // Select the entire title textfield contents when the bubble is first shown.
70 bookmark_bubble_->title_tf_->SelectAll(true);
71 bookmark_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
73 if (bookmark_bubble_->observer_)
74 bookmark_bubble_->observer_->OnBookmarkBubbleShown(url);
77 // static
78 bool BookmarkBubbleView::IsShowing() {
79 return bookmark_bubble_ != NULL;
82 void BookmarkBubbleView::Hide() {
83 if (IsShowing())
84 bookmark_bubble_->GetWidget()->Close();
87 BookmarkBubbleView::~BookmarkBubbleView() {
88 if (apply_edits_) {
89 ApplyEdits();
90 } else if (remove_bookmark_) {
91 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
92 const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(url_);
93 if (node)
94 model->Remove(node->parent(), node->parent()->GetIndexOf(node));
96 // |parent_combobox_| needs to be destroyed before |parent_model_| as it
97 // uses |parent_model_| in its destructor.
98 delete parent_combobox_;
101 views::View* BookmarkBubbleView::GetInitiallyFocusedView() {
102 return title_tf_;
105 void BookmarkBubbleView::WindowClosing() {
106 // We have to reset |bubble_| here, not in our destructor, because we'll be
107 // destroyed asynchronously and the shown state will be checked before then.
108 DCHECK_EQ(bookmark_bubble_, this);
109 bookmark_bubble_ = NULL;
111 if (observer_)
112 observer_->OnBookmarkBubbleHidden();
115 bool BookmarkBubbleView::AcceleratorPressed(
116 const ui::Accelerator& accelerator) {
117 if (accelerator.key_code() == ui::VKEY_RETURN) {
118 if (edit_button_->HasFocus())
119 HandleButtonPressed(edit_button_);
120 else
121 HandleButtonPressed(close_button_);
122 return true;
123 } else if (accelerator.key_code() == ui::VKEY_ESCAPE) {
124 remove_bookmark_ = newly_bookmarked_;
125 apply_edits_ = false;
128 return BubbleDelegateView::AcceleratorPressed(accelerator);
131 void BookmarkBubbleView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
132 views::BubbleDelegateView::OnNativeThemeChanged(theme);
133 const SkColor background_color = theme->GetSystemColor(
134 ui::NativeTheme::kColorId_DialogBackground);
135 set_color(background_color);
136 set_background(views::Background::CreateSolidBackground(background_color));
139 void BookmarkBubbleView::Init() {
140 views::Label* title_label = new views::Label(
141 l10n_util::GetStringUTF16(
142 newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
143 IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK));
144 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
145 title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
147 remove_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(
148 IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK));
149 remove_button_->SetStyle(views::Button::STYLE_BUTTON);
151 edit_button_ = new views::LabelButton(
152 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS));
153 edit_button_->SetStyle(views::Button::STYLE_BUTTON);
155 close_button_ = new views::LabelButton(
156 this, l10n_util::GetStringUTF16(IDS_DONE));
157 close_button_->SetStyle(views::Button::STYLE_BUTTON);
158 close_button_->SetIsDefault(true);
160 views::Label* combobox_label = new views::Label(
161 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_FOLDER_TEXT));
163 parent_combobox_ = new views::Combobox(&parent_model_);
164 parent_combobox_->set_listener(this);
165 parent_combobox_->SetAccessibleName(
166 l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_FOLDER_TEXT));
168 GridLayout* layout = new GridLayout(this);
169 SetLayoutManager(layout);
171 // Column sets used in the layout of the bubble.
172 enum ColumnSetID {
173 TITLE_COLUMN_SET_ID,
174 CONTENT_COLUMN_SET_ID,
175 SYNC_PROMO_COLUMN_SET_ID
178 ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID);
179 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
180 cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
181 0, 0);
182 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
184 // The column layout used for middle and bottom rows.
185 cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID);
186 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
187 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
188 GridLayout::USE_PREF, 0, 0);
189 cs->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing);
191 cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
192 GridLayout::USE_PREF, 0, 0);
193 cs->AddPaddingColumn(1, views::kUnrelatedControlLargeHorizontalSpacing);
195 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
196 GridLayout::USE_PREF, 0, 0);
197 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
198 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
199 GridLayout::USE_PREF, 0, 0);
200 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
202 layout->StartRow(0, TITLE_COLUMN_SET_ID);
203 layout->AddView(title_label);
204 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
206 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
207 views::Label* label = new views::Label(
208 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_TITLE_TEXT));
209 layout->AddView(label);
210 title_tf_ = new views::Textfield();
211 title_tf_->SetText(GetTitle());
212 title_tf_->SetAccessibleName(
213 l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_TITLE_TEXT));
215 layout->AddView(title_tf_, 5, 1);
217 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
219 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
220 layout->AddView(combobox_label);
221 layout->AddView(parent_combobox_, 5, 1);
223 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
225 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
226 layout->SkipColumns(2);
227 layout->AddView(remove_button_);
228 layout->AddView(edit_button_);
229 layout->AddView(close_button_);
231 layout->AddPaddingRow(
233 views::kUnrelatedControlVerticalSpacing - kControlBorderWidth);
235 if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
236 // The column layout used for the sync promo.
237 cs = layout->AddColumnSet(SYNC_PROMO_COLUMN_SET_ID);
238 cs->AddColumn(GridLayout::FILL,
239 GridLayout::FILL,
241 GridLayout::USE_PREF,
244 layout->StartRow(0, SYNC_PROMO_COLUMN_SET_ID);
246 sync_promo_view_ = new BookmarkSyncPromoView(delegate_.get());
247 layout->AddView(sync_promo_view_);
250 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
253 BookmarkBubbleView::BookmarkBubbleView(
254 views::View* anchor_view,
255 BookmarkBubbleViewObserver* observer,
256 scoped_ptr<BookmarkBubbleDelegate> delegate,
257 Profile* profile,
258 const GURL& url,
259 bool newly_bookmarked)
260 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
261 observer_(observer),
262 delegate_(delegate.Pass()),
263 profile_(profile),
264 url_(url),
265 newly_bookmarked_(newly_bookmarked),
266 parent_model_(
267 BookmarkModelFactory::GetForProfile(profile_),
268 BookmarkModelFactory::GetForProfile(profile_)->
269 GetMostRecentlyAddedNodeForURL(url)),
270 remove_button_(NULL),
271 edit_button_(NULL),
272 close_button_(NULL),
273 title_tf_(NULL),
274 parent_combobox_(NULL),
275 sync_promo_view_(NULL),
276 remove_bookmark_(false),
277 apply_edits_(true) {
278 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0));
279 // Compensate for built-in vertical padding in the anchor view's image.
280 set_anchor_view_insets(gfx::Insets(2, 0, 2, 0));
283 base::string16 BookmarkBubbleView::GetTitle() {
284 BookmarkModel* bookmark_model =
285 BookmarkModelFactory::GetForProfile(profile_);
286 const BookmarkNode* node =
287 bookmark_model->GetMostRecentlyAddedNodeForURL(url_);
288 if (node)
289 return node->GetTitle();
290 else
291 NOTREACHED();
292 return base::string16();
295 gfx::Size BookmarkBubbleView::GetMinimumSize() const {
296 gfx::Size size(views::BubbleDelegateView::GetPreferredSize());
297 size.SetToMax(gfx::Size(kMinBubbleWidth, 0));
298 return size;
301 void BookmarkBubbleView::GetAccessibleState(ui::AXViewState* state) {
302 BubbleDelegateView::GetAccessibleState(state);
303 state->name =
304 l10n_util::GetStringUTF16(
305 newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
306 IDS_BOOKMARK_AX_BUBBLE_PAGE_BOOKMARK);
309 void BookmarkBubbleView::ButtonPressed(views::Button* sender,
310 const ui::Event& event) {
311 HandleButtonPressed(sender);
314 void BookmarkBubbleView::OnPerformAction(views::Combobox* combobox) {
315 if (combobox->selected_index() + 1 == parent_model_.GetItemCount()) {
316 content::RecordAction(UserMetricsAction("BookmarkBubble_EditFromCombobox"));
317 ShowEditor();
321 void BookmarkBubbleView::HandleButtonPressed(views::Button* sender) {
322 if (sender == remove_button_) {
323 content::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"));
324 // Set this so we remove the bookmark after the window closes.
325 remove_bookmark_ = true;
326 apply_edits_ = false;
327 GetWidget()->Close();
328 } else if (sender == edit_button_) {
329 content::RecordAction(UserMetricsAction("BookmarkBubble_Edit"));
330 ShowEditor();
331 } else {
332 DCHECK_EQ(close_button_, sender);
333 GetWidget()->Close();
337 void BookmarkBubbleView::ShowEditor() {
338 const BookmarkNode* node = BookmarkModelFactory::GetForProfile(
339 profile_)->GetMostRecentlyAddedNodeForURL(url_);
340 views::Widget* parent = anchor_widget();
341 DCHECK(parent);
343 Profile* profile = profile_;
344 ApplyEdits();
345 GetWidget()->Close();
347 if (node && parent)
348 BookmarkEditor::Show(parent->GetNativeWindow(), profile,
349 BookmarkEditor::EditDetails::EditNode(node),
350 BookmarkEditor::SHOW_TREE);
353 void BookmarkBubbleView::ApplyEdits() {
354 // Set this to make sure we don't attempt to apply edits again.
355 apply_edits_ = false;
357 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
358 const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(url_);
359 if (node) {
360 const base::string16 new_title = title_tf_->text();
361 if (new_title != node->GetTitle()) {
362 model->SetTitle(node, new_title);
363 content::RecordAction(
364 UserMetricsAction("BookmarkBubble_ChangeTitleInBubble"));
366 parent_model_.MaybeChangeParent(node, parent_combobox_->selected_index());