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