Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / ui / views / bookmarks / bookmark_bubble_view.cc
blob3422e7a2a66c0b5aeaa0533a09118ff9734fc328
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.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/bookmarks/bookmark_utils.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
16 #include "chrome/browser/ui/sync/sync_promo_ui.h"
17 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view_observer.h"
18 #include "chrome/browser/ui/views/bookmarks/bookmark_sync_promo_view.h"
19 #include "content/public/browser/user_metrics.h"
20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h"
22 #include "ui/base/keycodes/keyboard_codes.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.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 content::UserMetricsAction;
36 using views::ColumnSet;
37 using views::GridLayout;
39 namespace {
41 // Minimum width of the the bubble.
42 const int kMinBubbleWidth = 350;
44 // Width of the border of a button.
45 const int kControlBorderWidth = 2;
47 } // namespace
49 // Declared in browser_dialogs.h so callers don't have to depend on our header.
51 namespace chrome {
53 void ShowBookmarkBubbleView(views::View* anchor_view,
54 BookmarkBubbleViewObserver* observer,
55 scoped_ptr<BookmarkBubbleDelegate> delegate,
56 Profile* profile,
57 const GURL& url,
58 bool newly_bookmarked) {
59 BookmarkBubbleView::ShowBubble(anchor_view,
60 observer,
61 delegate.Pass(),
62 profile,
63 url,
64 newly_bookmarked);
67 void HideBookmarkBubbleView() {
68 BookmarkBubbleView::Hide();
71 bool IsBookmarkBubbleViewShowing() {
72 return BookmarkBubbleView::IsShowing();
75 } // namespace chrome
77 // BookmarkBubbleView ---------------------------------------------------------
79 BookmarkBubbleView* BookmarkBubbleView::bookmark_bubble_ = NULL;
81 // static
82 void BookmarkBubbleView::ShowBubble(
83 views::View* anchor_view,
84 BookmarkBubbleViewObserver* observer,
85 scoped_ptr<BookmarkBubbleDelegate> delegate,
86 Profile* profile,
87 const GURL& url,
88 bool newly_bookmarked) {
89 if (IsShowing())
90 return;
92 bookmark_bubble_ = new BookmarkBubbleView(anchor_view,
93 observer,
94 delegate.Pass(),
95 profile,
96 url,
97 newly_bookmarked);
98 views::BubbleDelegateView::CreateBubble(bookmark_bubble_)->Show();
99 // Select the entire title textfield contents when the bubble is first shown.
100 bookmark_bubble_->title_tf_->SelectAll(true);
101 bookmark_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
103 if (bookmark_bubble_->observer_)
104 bookmark_bubble_->observer_->OnBookmarkBubbleShown(url);
107 // static
108 bool BookmarkBubbleView::IsShowing() {
109 return bookmark_bubble_ != NULL;
112 void BookmarkBubbleView::Hide() {
113 if (IsShowing())
114 bookmark_bubble_->GetWidget()->Close();
117 BookmarkBubbleView::~BookmarkBubbleView() {
118 if (apply_edits_) {
119 ApplyEdits();
120 } else if (remove_bookmark_) {
121 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
122 const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(url_);
123 if (node)
124 model->Remove(node->parent(), node->parent()->GetIndexOf(node));
128 views::View* BookmarkBubbleView::GetInitiallyFocusedView() {
129 return title_tf_;
132 void BookmarkBubbleView::WindowClosing() {
133 // We have to reset |bubble_| here, not in our destructor, because we'll be
134 // destroyed asynchronously and the shown state will be checked before then.
135 DCHECK_EQ(bookmark_bubble_, this);
136 bookmark_bubble_ = NULL;
138 if (observer_)
139 observer_->OnBookmarkBubbleHidden();
142 bool BookmarkBubbleView::AcceleratorPressed(
143 const ui::Accelerator& accelerator) {
144 if (accelerator.key_code() == ui::VKEY_RETURN) {
145 if (edit_button_->HasFocus())
146 HandleButtonPressed(edit_button_);
147 else
148 HandleButtonPressed(close_button_);
149 return true;
150 } else if (accelerator.key_code() == ui::VKEY_ESCAPE) {
151 remove_bookmark_ = newly_bookmarked_;
152 apply_edits_ = false;
155 return BubbleDelegateView::AcceleratorPressed(accelerator);
158 void BookmarkBubbleView::Init() {
159 views::Label* title_label = new views::Label(
160 l10n_util::GetStringUTF16(
161 newly_bookmarked_ ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED :
162 IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK));
163 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
164 title_label->SetFont(rb.GetFont(ui::ResourceBundle::MediumFont));
166 remove_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(
167 IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK));
168 remove_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
170 edit_button_ = new views::LabelButton(
171 this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS));
172 edit_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
174 close_button_ = new views::LabelButton(
175 this, l10n_util::GetStringUTF16(IDS_DONE));
176 close_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
177 close_button_->SetIsDefault(true);
179 views::Label* combobox_label = new views::Label(
180 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_FOLDER_TEXT));
182 parent_combobox_ = new views::Combobox(&parent_model_);
183 parent_combobox_->set_listener(this);
184 parent_combobox_->SetAccessibleName(combobox_label->text());
186 GridLayout* layout = new GridLayout(this);
187 SetLayoutManager(layout);
189 // Column sets used in the layout of the bubble.
190 enum ColumnSetID {
191 TITLE_COLUMN_SET_ID,
192 CONTENT_COLUMN_SET_ID,
193 SYNC_PROMO_COLUMN_SET_ID
196 ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID);
197 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
198 cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
199 0, 0);
200 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
202 // The column layout used for middle and bottom rows.
203 cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID);
204 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
205 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
206 GridLayout::USE_PREF, 0, 0);
207 cs->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing);
209 cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
210 GridLayout::USE_PREF, 0, 0);
211 cs->AddPaddingColumn(1, views::kUnrelatedControlLargeHorizontalSpacing);
213 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
214 GridLayout::USE_PREF, 0, 0);
215 cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
216 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
217 GridLayout::USE_PREF, 0, 0);
218 cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
220 layout->StartRow(0, TITLE_COLUMN_SET_ID);
221 layout->AddView(title_label);
222 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
224 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
225 views::Label* label = new views::Label(
226 l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_TITLE_TEXT));
227 layout->AddView(label);
228 title_tf_ = new views::Textfield();
229 title_tf_->SetText(GetTitle());
230 layout->AddView(title_tf_, 5, 1);
232 layout->AddPaddingRow(0, views::kUnrelatedControlHorizontalSpacing);
234 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
235 layout->AddView(combobox_label);
236 layout->AddView(parent_combobox_, 5, 1);
238 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
240 layout->StartRow(0, CONTENT_COLUMN_SET_ID);
241 layout->SkipColumns(2);
242 layout->AddView(remove_button_);
243 layout->AddView(edit_button_);
244 layout->AddView(close_button_);
246 layout->AddPaddingRow(
248 views::kUnrelatedControlVerticalSpacing - kControlBorderWidth);
250 if (SyncPromoUI::ShouldShowSyncPromo(profile_)) {
251 // The column layout used for the sync promo.
252 cs = layout->AddColumnSet(SYNC_PROMO_COLUMN_SET_ID);
253 cs->AddColumn(GridLayout::FILL,
254 GridLayout::FILL,
256 GridLayout::USE_PREF,
259 layout->StartRow(0, SYNC_PROMO_COLUMN_SET_ID);
261 sync_promo_view_ = new BookmarkSyncPromoView(delegate_.get());
262 layout->AddView(sync_promo_view_);
265 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
268 BookmarkBubbleView::BookmarkBubbleView(
269 views::View* anchor_view,
270 BookmarkBubbleViewObserver* observer,
271 scoped_ptr<BookmarkBubbleDelegate> delegate,
272 Profile* profile,
273 const GURL& url,
274 bool newly_bookmarked)
275 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
276 observer_(observer),
277 delegate_(delegate.Pass()),
278 profile_(profile),
279 url_(url),
280 newly_bookmarked_(newly_bookmarked),
281 parent_model_(
282 BookmarkModelFactory::GetForProfile(profile_),
283 BookmarkModelFactory::GetForProfile(profile_)->
284 GetMostRecentlyAddedNodeForURL(url)),
285 remove_button_(NULL),
286 edit_button_(NULL),
287 close_button_(NULL),
288 title_tf_(NULL),
289 parent_combobox_(NULL),
290 sync_promo_view_(NULL),
291 remove_bookmark_(false),
292 apply_edits_(true) {
293 const SkColor background_color = GetNativeTheme()->GetSystemColor(
294 ui::NativeTheme::kColorId_DialogBackground);
295 set_color(background_color);
296 set_background(views::Background::CreateSolidBackground(background_color));
297 set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0));
298 // Compensate for built-in vertical padding in the anchor view's image.
299 set_anchor_view_insets(gfx::Insets(7, 0, 7, 0));
302 string16 BookmarkBubbleView::GetTitle() {
303 BookmarkModel* bookmark_model =
304 BookmarkModelFactory::GetForProfile(profile_);
305 const BookmarkNode* node =
306 bookmark_model->GetMostRecentlyAddedNodeForURL(url_);
307 if (node)
308 return node->GetTitle();
309 else
310 NOTREACHED();
311 return string16();
314 gfx::Size BookmarkBubbleView::GetMinimumSize() {
315 gfx::Size size(views::BubbleDelegateView::GetPreferredSize());
316 size.SetToMax(gfx::Size(kMinBubbleWidth, 0));
317 return size;
320 void BookmarkBubbleView::ButtonPressed(views::Button* sender,
321 const ui::Event& event) {
322 HandleButtonPressed(sender);
325 void BookmarkBubbleView::OnSelectedIndexChanged(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 StartFade(false);
339 } else if (sender == edit_button_) {
340 content::RecordAction(UserMetricsAction("BookmarkBubble_Edit"));
341 ShowEditor();
342 } else {
343 DCHECK_EQ(close_button_, sender);
344 StartFade(false);
348 void BookmarkBubbleView::ShowEditor() {
349 const BookmarkNode* node = BookmarkModelFactory::GetForProfile(
350 profile_)->GetMostRecentlyAddedNodeForURL(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->GetMostRecentlyAddedNodeForURL(url_);
370 if (node) {
371 const 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());