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