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