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
32 using views::ColumnSet;
33 using views::GridLayout;
34
35 namespace {
36
37 // Minimum width of the the bubble.
38 const int kMinBubbleWidth = 300;
39 // Minimum width of the the textfield.
40 const int kMinTextfieldWidth = 200;
41 // Size of the icon.
42 const int kIconSize = extension_misc::EXTENSION_ICON_MEDIUM;
43
44 } // namespace
45
46 BookmarkAppBubbleView* BookmarkAppBubbleView::bookmark_app_bubble_ = NULL;
47
~BookmarkAppBubbleView()48 BookmarkAppBubbleView::~BookmarkAppBubbleView() {
49 }
50
51 // static
ShowBubble(views::View * anchor_view,Profile * profile,const WebApplicationInfo & web_app_info,const std::string & extension_id)52 void 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
BookmarkAppBubbleView(views::View * anchor_view,Profile * profile,const WebApplicationInfo & web_app_info,const std::string & extension_id)67 BookmarkAppBubbleView::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
Init()92 void 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
GetInitiallyFocusedView()190 views::View* BookmarkAppBubbleView::GetInitiallyFocusedView() {
191 return title_tf_;
192 }
193
WindowClosing()194 void 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
AcceleratorPressed(const ui::Accelerator & accelerator)209 bool 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
GetMinimumSize() const218 gfx::Size BookmarkAppBubbleView::GetMinimumSize() const {
219 gfx::Size size(views::BubbleDelegateView::GetPreferredSize());
220 size.SetToMax(gfx::Size(kMinBubbleWidth, 0));
221 return size;
222 }
223
ButtonPressed(views::Button * sender,const ui::Event & event)224 void BookmarkAppBubbleView::ButtonPressed(views::Button* sender,
225 const ui::Event& event) {
226 HandleButtonPressed(sender);
227 }
228
SetAppImage(const std::string & id,const gfx::ImageSkia & image)229 void 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
HandleButtonPressed(views::Button * sender)235 void 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
ApplyEdits()244 void 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