Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bubble_view.cc
blob98a9bc6ee418264ccca2ecabdb748d4797ad27ea
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 "chrome/grit/generated_resources.h"
18 #include "components/bookmarks/browser/bookmark_model.h"
19 #include "components/bookmarks/browser/bookmark_utils.h"
20 #include "content/public/browser/user_metrics.h"
21 #include "ui/accessibility/ax_view_state.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/events/keycodes/keyboard_codes.h"
25 #include "ui/views/bubble/bubble_frame_view.h"
26 #include "ui/views/controls/button/label_button.h"
27 #include "ui/views/controls/combobox/combobox.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/controls/link.h"
30 #include "ui/views/controls/textfield/textfield.h"
31 #include "ui/views/layout/grid_layout.h"
32 #include "ui/views/layout/layout_constants.h"
33 #include "ui/views/widget/widget.h"
35 using base::UserMetricsAction;
36 using bookmarks::BookmarkModel;
37 using bookmarks::BookmarkNode;
38 using views::ColumnSet;
39 using views::GridLayout;
41 namespace {
43 // Width of the border of a button.
44 const int kControlBorderWidth = 2;
46 // This combobox prevents any lengthy content from stretching the bubble view.
47 class UnsizedCombobox : public views::Combobox {
48 public:
49 explicit UnsizedCombobox(ui::ComboboxModel* model) : views::Combobox(model) {}
50 ~UnsizedCombobox() override {}
52 gfx::Size GetPreferredSize() const override {
53 return gfx::Size(0, views::Combobox::GetPreferredSize().height());
56 private:
57 DISALLOW_COPY_AND_ASSIGN(UnsizedCombobox);
60 } // namespace
62 BookmarkBubbleView* BookmarkBubbleView::bookmark_bubble_ = NULL;
64 // static
65 void BookmarkBubbleView::ShowBubble(views::View* anchor_view,
66 BookmarkBubbleViewObserver* observer,
67 scoped_ptr<BookmarkBubbleDelegate> delegate,
68 Profile* profile,
69 const GURL& url,
70 bool newly_bookmarked) {
71 if (IsShowing())
72 return;
74 bookmark_bubble_ = new BookmarkBubbleView(anchor_view,
75 observer,
76 delegate.Pass(),
77 profile,
78 url,
79 newly_bookmarked);
80 views::BubbleDelegateView::CreateBubble(bookmark_bubble_)->Show();
81 // Select the entire title textfield contents when the bubble is first shown.
82 bookmark_bubble_->title_tf_->SelectAll(true);
83 bookmark_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
85 if (bookmark_bubble_->observer_)
86 bookmark_bubble_->observer_->OnBookmarkBubbleShown(url);
89 // static
90 bool BookmarkBubbleView::IsShowing() {
91 return bookmark_bubble_ != NULL;
94 void BookmarkBubbleView::Hide() {
95 if (IsShowing())
96 bookmark_bubble_->GetWidget()->Close();
99 BookmarkBubbleView::~BookmarkBubbleView() {
100 if (apply_edits_) {
101 ApplyEdits();
102 } else if (remove_bookmark_) {
103 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
104 const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(url_);
105 if (node)
106 model->Remove(node->parent(), node->parent()->GetIndexOf(node));
108 // |parent_combobox_| needs to be destroyed before |parent_model_| as it
109 // uses |parent_model_| in its destructor.
110 delete parent_combobox_;
113 void BookmarkBubbleView::WindowClosing() {
114 // We have to reset |bubble_| here, not in our destructor, because we'll be
115 // destroyed asynchronously and the shown state will be checked before then.
116 DCHECK_EQ(bookmark_bubble_, this);
117 bookmark_bubble_ = NULL;
119 if (observer_)
120 observer_->OnBookmarkBubbleHidden();
123 bool BookmarkBubbleView::AcceleratorPressed(
124 const ui::Accelerator& accelerator) {
125 ui::KeyboardCode key_code = accelerator.key_code();
126 if (key_code == ui::VKEY_RETURN) {
127 HandleButtonPressed(close_button_);
128 return true;
130 if (key_code == ui::VKEY_E && accelerator.IsAltDown()) {
131 HandleButtonPressed(edit_button_);
132 return true;
134 if (key_code == ui::VKEY_R && accelerator.IsAltDown()) {
135 HandleButtonPressed(remove_button_);
136 return true;
138 if (key_code == ui::VKEY_ESCAPE) {
139 remove_bookmark_ = newly_bookmarked_;
140 apply_edits_ = false;
143 return BubbleDelegateView::AcceleratorPressed(accelerator);
146 void BookmarkBubbleView::Init() {
147 views::Label* title_label = new views::Label(
148 l10n_util::GetStringUTF16(
149 newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
150 IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK));
151 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
152 title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
154 remove_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(
155 IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK));
156 remove_button_->SetStyle(views::Button::STYLE_BUTTON);
158 edit_button_ = new views::LabelButton(
159 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS));
160 edit_button_->SetStyle(views::Button::STYLE_BUTTON);
162 close_button_ = new views::LabelButton(
163 this, l10n_util::GetStringUTF16(IDS_DONE));
164 close_button_->SetStyle(views::Button::STYLE_BUTTON);
165 close_button_->SetIsDefault(true);
167 views::Label* combobox_label = new views::Label(
168 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_FOLDER_TEXT));
170 parent_combobox_ = new UnsizedCombobox(&parent_model_);
171 parent_combobox_->set_listener(this);
172 parent_combobox_->SetAccessibleName(
173 l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_FOLDER_TEXT));
175 GridLayout* layout = new GridLayout(this);
176 SetLayoutManager(layout);
178 // Column sets used in the layout of the bubble.
179 enum ColumnSetID {
180 TITLE_COLUMN_SET_ID,
181 CONTENT_COLUMN_SET_ID,
182 SYNC_PROMO_COLUMN_SET_ID
185 ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID);
186 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
187 cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
188 0, 0);
189 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
191 // The column layout used for middle and bottom rows.
192 cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID);
193 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
194 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
195 GridLayout::USE_PREF, 0, 0);
196 cs->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing);
198 cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
199 GridLayout::USE_PREF, 0, 0);
200 cs->AddPaddingColumn(1, views::kUnrelatedControlLargeHorizontalSpacing);
202 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
203 GridLayout::USE_PREF, 0, 0);
204 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
205 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
206 GridLayout::USE_PREF, 0, 0);
207 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
209 layout->StartRow(0, TITLE_COLUMN_SET_ID);
210 layout->AddView(title_label);
211 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
213 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
214 views::Label* label = new views::Label(
215 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_TITLE_TEXT));
216 layout->AddView(label);
217 title_tf_ = new views::Textfield();
218 title_tf_->SetText(GetTitle());
219 title_tf_->SetAccessibleName(
220 l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_TITLE_TEXT));
222 layout->AddView(title_tf_, 5, 1);
224 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
226 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
227 layout->AddView(combobox_label);
228 layout->AddView(parent_combobox_, 5, 1);
230 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
232 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
233 layout->SkipColumns(2);
234 layout->AddView(remove_button_);
235 layout->AddView(edit_button_);
236 layout->AddView(close_button_);
238 layout->AddPaddingRow(
240 views::kUnrelatedControlVerticalSpacing - kControlBorderWidth);
242 if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
243 // The column layout used for the sync promo.
244 cs = layout->AddColumnSet(SYNC_PROMO_COLUMN_SET_ID);
245 cs->AddColumn(GridLayout::FILL,
246 GridLayout::FILL,
248 GridLayout::USE_PREF,
251 layout->StartRow(0, SYNC_PROMO_COLUMN_SET_ID);
253 sync_promo_view_ = new BookmarkSyncPromoView(delegate_.get());
254 layout->AddView(sync_promo_view_);
257 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
258 AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN));
259 AddAccelerator(ui::Accelerator(ui::VKEY_R, ui::EF_ALT_DOWN));
262 const char* BookmarkBubbleView::GetClassName() const {
263 return "BookmarkBubbleView";
266 views::View* BookmarkBubbleView::GetInitiallyFocusedView() {
267 return title_tf_;
270 BookmarkBubbleView::BookmarkBubbleView(
271 views::View* anchor_view,
272 BookmarkBubbleViewObserver* observer,
273 scoped_ptr<BookmarkBubbleDelegate> delegate,
274 Profile* profile,
275 const GURL& url,
276 bool newly_bookmarked)
277 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
278 observer_(observer),
279 delegate_(delegate.Pass()),
280 profile_(profile),
281 url_(url),
282 newly_bookmarked_(newly_bookmarked),
283 parent_model_(
284 BookmarkModelFactory::GetForProfile(profile_),
285 BookmarkModelFactory::GetForProfile(profile_)->
286 GetMostRecentlyAddedUserNodeForURL(url)),
287 remove_button_(NULL),
288 edit_button_(NULL),
289 close_button_(NULL),
290 title_tf_(NULL),
291 parent_combobox_(NULL),
292 sync_promo_view_(NULL),
293 remove_bookmark_(false),
294 apply_edits_(true) {
295 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0));
296 // Compensate for built-in vertical padding in the anchor view's image.
297 set_anchor_view_insets(gfx::Insets(2, 0, 2, 0));
300 base::string16 BookmarkBubbleView::GetTitle() {
301 BookmarkModel* bookmark_model =
302 BookmarkModelFactory::GetForProfile(profile_);
303 const BookmarkNode* node =
304 bookmark_model->GetMostRecentlyAddedUserNodeForURL(url_);
305 if (node)
306 return node->GetTitle();
307 else
308 NOTREACHED();
309 return base::string16();
312 void BookmarkBubbleView::GetAccessibleState(ui::AXViewState* state) {
313 BubbleDelegateView::GetAccessibleState(state);
314 state->name =
315 l10n_util::GetStringUTF16(
316 newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
317 IDS_BOOKMARK_AX_BUBBLE_PAGE_BOOKMARK);
320 void BookmarkBubbleView::ButtonPressed(views::Button* sender,
321 const ui::Event& event) {
322 HandleButtonPressed(sender);
325 void BookmarkBubbleView::OnPerformAction(views::Combobox* combobox) {
326 if (combobox->selected_index() + 1 == parent_model_.GetItemCount()) {
327 content::RecordAction(UserMetricsAction("BookmarkBubble_EditFromCombobox"));
328 ShowEditor();
332 void BookmarkBubbleView::HandleButtonPressed(views::Button* sender) {
333 if (sender == remove_button_) {
334 content::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"));
335 // Set this so we remove the bookmark after the window closes.
336 remove_bookmark_ = true;
337 apply_edits_ = false;
338 GetWidget()->Close();
339 } else if (sender == edit_button_) {
340 content::RecordAction(UserMetricsAction("BookmarkBubble_Edit"));
341 ShowEditor();
342 } else {
343 DCHECK_EQ(close_button_, sender);
344 GetWidget()->Close();
348 void BookmarkBubbleView::ShowEditor() {
349 const BookmarkNode* node = BookmarkModelFactory::GetForProfile(
350 profile_)->GetMostRecentlyAddedUserNodeForURL(url_);
351 views::Widget* parent = anchor_widget();
352 DCHECK(parent);
354 Profile* profile = profile_;
355 ApplyEdits();
356 GetWidget()->Close();
358 if (node && parent)
359 BookmarkEditor::Show(parent->GetNativeWindow(), profile,
360 BookmarkEditor::EditDetails::EditNode(node),
361 BookmarkEditor::SHOW_TREE);
364 void BookmarkBubbleView::ApplyEdits() {
365 // Set this to make sure we don't attempt to apply edits again.
366 apply_edits_ = false;
368 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
369 const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(url_);
370 if (node) {
371 const base::string16 new_title = title_tf_->text();
372 if (new_title != node->GetTitle()) {
373 model->SetTitle(node, new_title);
374 content::RecordAction(
375 UserMetricsAction("BookmarkBubble_ChangeTitleInBubble"));
377 parent_model_.MaybeChangeParent(node, parent_combobox_->selected_index());