bookmark_app_bubble_view.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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/extensions/bookmark_app_bubble_view.h"
6
7#include "base/strings/string16.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/extensions/app_icon_loader_impl.h"
10#include "chrome/browser/extensions/bookmark_app_helper.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/extensions/launch_util.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/extensions/extension_constants.h"
15#include "extensions/browser/extension_prefs.h"
16#include "extensions/browser/extension_registry.h"
17#include "extensions/browser/extension_system.h"
18#include "extensions/browser/pref_names.h"
19#include "extensions/browser/uninstall_reason.h"
20#include "extensions/common/constants.h"
21#include "grit/generated_resources.h"
22#include "grit/theme_resources.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/controls/button/checkbox.h"
27#include "ui/views/controls/button/label_button.h"
28#include "ui/views/controls/image_view.h"
29#include "ui/views/controls/label.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"
34
35using views::ColumnSet;
36using views::GridLayout;
37
38namespace {
39
40// Minimum width of the the bubble.
41const int kMinBubbleWidth = 300;
42// Minimum width of the the textfield.
43const int kMinTextfieldWidth = 200;
44// Size of the icon.
45const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM;
46
47ExtensionService* GetExtensionService(Profile* profile) {
48  return extensions::ExtensionSystem::Get(profile)->extension_service();
49}
50
51}  // namespace
52
53BookmarkAppBubbleView* BookmarkAppBubbleView::bookmark_app_bubble_ = NULL;
54
55BookmarkAppBubbleView::~BookmarkAppBubbleView() {
56}
57
58// static
59void BookmarkAppBubbleView::ShowBubble(views::View* anchor_view,
60                                       Profile* profile,
61                                       const WebApplicationInfo& web_app_info,
62                                       const std::string& extension_id) {
63  if (bookmark_app_bubble_ != NULL)
64    return;
65
66  bookmark_app_bubble_ = new BookmarkAppBubbleView(
67      anchor_view, profile, web_app_info, extension_id);
68  views::BubbleDelegateView::CreateBubble(bookmark_app_bubble_)->Show();
69  // Select the entire title textfield contents when the bubble is first shown.
70  bookmark_app_bubble_->title_tf_->SelectAll(true);
71  bookmark_app_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
72}
73
74BookmarkAppBubbleView::BookmarkAppBubbleView(
75    views::View* anchor_view,
76    Profile* profile,
77    const WebApplicationInfo& web_app_info,
78    const std::string& extension_id)
79    : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
80      profile_(profile),
81      web_app_info_(web_app_info),
82      extension_id_(extension_id),
83      add_button_(NULL),
84      cancel_button_(NULL),
85      open_as_tab_checkbox_(NULL),
86      title_tf_(NULL),
87      remove_app_(true),
88      app_icon_loader_(new extensions::AppIconLoaderImpl(profile,
89                                                         kIconSize,
90                                                         this)) {
91  const SkColor background_color = GetNativeTheme()->GetSystemColor(
92      ui::NativeTheme::kColorId_DialogBackground);
93  set_arrow(views::BubbleBorder::TOP_CENTER);
94  set_color(background_color);
95  set_background(views::Background::CreateSolidBackground(background_color));
96  set_margins(gfx::Insets(views::kPanelVertMargin, 0, 0, 0));
97}
98
99void BookmarkAppBubbleView::Init() {
100  views::Label* title_label = new views::Label(
101      l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_TITLE));
102  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
103  title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
104  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
105
106  add_button_ =
107      new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_ADD));
108  add_button_->SetStyle(views::Button::STYLE_BUTTON);
109  add_button_->SetIsDefault(true);
110
111  cancel_button_ =
112      new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL));
113  cancel_button_->SetStyle(views::Button::STYLE_BUTTON);
114
115  GridLayout* layout = new GridLayout(this);
116  SetLayoutManager(layout);
117
118  // Column sets used in the layout of the bubble.
119  enum ColumnSetID {
120    TITLE_COLUMN_SET_ID,
121    TITLE_TEXT_COLUMN_SET_ID,
122    CONTENT_COLUMN_SET_ID
123  };
124
125  // The column layout used for the title and checkbox.
126  ColumnSet* cs = layout->AddColumnSet(TITLE_COLUMN_SET_ID);
127  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
128  cs->AddColumn(
129      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
130  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
131
132  // The column layout used for the icon and text box.
133  cs = layout->AddColumnSet(TITLE_TEXT_COLUMN_SET_ID);
134  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
135  cs->AddColumn(GridLayout::LEADING,
136                GridLayout::CENTER,
137                0,
138                GridLayout::USE_PREF,
139                0,
140                0);
141  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
142  cs->AddColumn(GridLayout::FILL,
143                GridLayout::CENTER,
144                1,
145                GridLayout::USE_PREF,
146                0,
147                kMinTextfieldWidth);
148  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
149
150  // The column layout used for the row with buttons.
151  cs = layout->AddColumnSet(CONTENT_COLUMN_SET_ID);
152  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
153  cs->AddColumn(
154      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
155  cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
156  cs->AddColumn(
157      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
158  cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
159  cs->AddColumn(
160      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
161  cs->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
162
163  layout->StartRow(0, TITLE_COLUMN_SET_ID);
164  layout->AddView(title_label);
165  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
166
167  const extensions::Extension* extension =
168      extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
169          extension_id_, extensions::ExtensionRegistry::EVERYTHING);
170
171  layout->StartRow(0, TITLE_TEXT_COLUMN_SET_ID);
172  icon_image_view_ = new views::ImageView();
173  icon_image_view_->SetImageSize(gfx::Size(kIconSize, kIconSize));
174  layout->AddView(icon_image_view_);
175  app_icon_loader_->FetchImage(extension_id_);
176
177  title_tf_ = new views::Textfield();
178  title_tf_->SetText(extension ? base::UTF8ToUTF16(extension->name())
179                               : web_app_info_.title);
180  layout->AddView(title_tf_);
181  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
182
183  layout->StartRow(0, CONTENT_COLUMN_SET_ID);
184  open_as_tab_checkbox_ = new views::Checkbox(
185      l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_TAB));
186  open_as_tab_checkbox_->SetChecked(
187      profile_->GetPrefs()->GetInteger(
188          extensions::pref_names::kBookmarkAppCreationLaunchType) ==
189              extensions::LAUNCH_TYPE_REGULAR);
190  layout->AddView(open_as_tab_checkbox_);
191  layout->AddView(add_button_);
192  layout->AddView(cancel_button_);
193  layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
194
195  AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
196}
197
198views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() {
199  return title_tf_;
200}
201
202void BookmarkAppBubbleView::WindowClosing() {
203  // We have to reset |bookmark_app_bubble_| here, not in our destructor,
204  // because we'll be destroyed asynchronously and the shown state will be
205  // checked before then.
206  DCHECK_EQ(bookmark_app_bubble_, this);
207  bookmark_app_bubble_ = NULL;
208
209  if (remove_app_) {
210    GetExtensionService(profile_)
211        ->UninstallExtension(extension_id_,
212                             extensions::UNINSTALL_REASON_INSTALL_CANCELED,
213                             base::Bind(&base::DoNothing),
214                             NULL);
215  } else {
216    ApplyEdits();
217  }
218}
219
220bool BookmarkAppBubbleView::AcceleratorPressed(
221    const ui::Accelerator& accelerator) {
222  if (accelerator.key_code() == ui::VKEY_RETURN) {
223    HandleButtonPressed(add_button_);
224  }
225
226  return BubbleDelegateView::AcceleratorPressed(accelerator);
227}
228
229gfx::Size BookmarkAppBubbleView::GetMinimumSize() const {
230  gfx::Size size(views::BubbleDelegateView::GetPreferredSize());
231  size.SetToMax(gfx::Size(kMinBubbleWidth, 0));
232  return size;
233}
234
235void BookmarkAppBubbleView::ButtonPressed(views::Button* sender,
236                                          const ui::Event& event) {
237  HandleButtonPressed(sender);
238}
239
240void BookmarkAppBubbleView::SetAppImage(const std::string& id,
241                                        const gfx::ImageSkia& image) {
242  DCHECK_EQ(extension_id_, id);
243  icon_image_view_->SetImage(image);
244}
245
246void BookmarkAppBubbleView::HandleButtonPressed(views::Button* sender) {
247  // Unset |remove_app_| so we don't delete the bookmark after the window
248  // closes.
249  if (sender == add_button_)
250    remove_app_ = false;
251
252  GetWidget()->Close();
253}
254
255void BookmarkAppBubbleView::ApplyEdits() {
256  // Set the launch type based on the checkbox.
257  extensions::LaunchType launch_type = open_as_tab_checkbox_->checked()
258      ? extensions::LAUNCH_TYPE_REGULAR
259      : extensions::LAUNCH_TYPE_WINDOW;
260  profile_->GetPrefs()->SetInteger(
261          extensions::pref_names::kBookmarkAppCreationLaunchType, launch_type);
262  extensions::SetLaunchType(GetExtensionService(profile_),
263                            extension_id_,
264                            launch_type);
265
266  const extensions::Extension* extension =
267      extensions::ExtensionRegistry::Get(profile_)->GetExtensionById(
268          extension_id_, extensions::ExtensionRegistry::EVERYTHING);
269  if (extension && base::UTF8ToUTF16(extension->name()) == title_tf_->text())
270    return;
271
272  // Reinstall the app with an updated name.
273  WebApplicationInfo install_info(web_app_info_);
274  install_info.title = title_tf_->text();
275
276  extensions::CreateOrUpdateBookmarkApp(GetExtensionService(profile_),
277                                        install_info);
278}
279