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