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