1 // Copyright (c) 2011 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/chromeos/login/captcha_view.h" 6 7 #include "base/string_util.h" 8 #include "base/utf_string_conversions.h" 9 #include "chrome/browser/chromeos/login/helper.h" 10 #include "chrome/browser/chromeos/login/image_downloader.h" 11 #include "chrome/browser/chromeos/login/rounded_rect_painter.h" 12 #include "chrome/browser/chromeos/login/textfield_with_margin.h" 13 #include "chrome/browser/chromeos/views/copy_background.h" 14 #include "grit/chromium_strings.h" 15 #include "grit/generated_resources.h" 16 #include "grit/locale_settings.h" 17 #include "ui/base/l10n/l10n_util.h" 18 #include "views/background.h" 19 #include "views/controls/button/text_button.h" 20 #include "views/controls/image_view.h" 21 #include "views/controls/label.h" 22 #include "views/controls/textfield/textfield.h" 23 #include "views/layout/grid_layout.h" 24 #include "views/layout/layout_constants.h" 25 #include "views/widget/widget_gtk.h" 26 #include "views/window/window.h" 27 28 using views::Label; 29 using views::Textfield; 30 using views::WidgetGtk; 31 32 namespace chromeos { 33 34 namespace { 35 36 // A Textfield for captcha input, which also sets focus to itself 37 // when a mouse is clicked on it. This is necessary in screen locker mode 38 // as mouse events are grabbed in the screen locker. 39 class CaptchaField : public TextfieldWithMargin { 40 public: 41 CaptchaField() 42 : TextfieldWithMargin() { 43 } 44 45 // views::View overrides. 46 virtual bool OnMousePressed(const views::MouseEvent& e) { 47 RequestFocus(); 48 return false; 49 } 50 51 private: 52 DISALLOW_COPY_AND_ASSIGN(CaptchaField); 53 }; 54 55 class WideTextButton : public views::TextButton { 56 public: 57 WideTextButton(views::ButtonListener* listener, const std::wstring& text) 58 : TextButton(listener, text) { 59 SetFont(font().DeriveFont(kFontSizeCorrectionDelta)); 60 } 61 62 virtual ~WideTextButton() {} 63 64 private: 65 virtual gfx::Size GetPreferredSize() { 66 gfx::Size preferred_size = TextButton::GetPreferredSize(); 67 // Set minimal width. 68 if (preferred_size.width() < login::kButtonMinWidth) 69 preferred_size.set_width(login::kButtonMinWidth); 70 return preferred_size; 71 } 72 73 DISALLOW_COPY_AND_ASSIGN(WideTextButton); 74 }; 75 76 } // namespace 77 78 CaptchaView::CaptchaView(const GURL& captcha_url, bool is_standalone) 79 : delegate_(NULL), 80 captcha_url_(captcha_url), 81 captcha_image_(NULL), 82 captcha_textfield_(NULL), 83 is_standalone_(is_standalone), 84 ok_button_(NULL) { 85 } 86 87 bool CaptchaView::Accept() { 88 if (delegate_) 89 delegate_->OnCaptchaEntered(UTF16ToUTF8(captcha_textfield_->text())); 90 return true; 91 } 92 93 std::wstring CaptchaView::GetWindowTitle() const { 94 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_CAPTCHA_DIALOG_TITLE)); 95 } 96 97 void CaptchaView::SetCaptchaURL(const GURL& captcha_url) { 98 captcha_url_ = captcha_url; 99 captcha_textfield_->SetText(string16()); 100 // ImageDownloader will delete itself once URL is fetched. 101 // TODO(nkostylev): Make sure that it works after view is deleted. 102 new ImageDownloader(this, GURL(captcha_url_), std::string()); 103 } 104 105 gfx::Size CaptchaView::GetPreferredSize() { 106 gfx::Size size = gfx::Size(views::Window::GetLocalizedContentsSize( 107 IDS_CAPTCHA_INPUT_DIALOG_WIDTH_CHARS, 108 IDS_CAPTCHA_INPUT_DIALOG_HEIGHT_LINES)); 109 if (is_standalone_) 110 size.set_height(size.height() + ok_button_->GetPreferredSize().height()); 111 112 return size; 113 } 114 115 void CaptchaView::ViewHierarchyChanged(bool is_add, 116 views::View* parent, 117 views::View* child) { 118 // Can't focus before we're inserted into a Container. 119 if (is_add && child == this) 120 captcha_textfield_->RequestFocus(); 121 } 122 123 bool CaptchaView::HandleKeyEvent(views::Textfield* sender, 124 const views::KeyEvent& key_event) { 125 if (sender == captcha_textfield_ && 126 key_event.key_code() == ui::VKEY_RETURN) { 127 if (is_standalone_) { 128 Accept(); 129 } else { 130 GetDialogClientView()->AcceptWindow(); 131 } 132 } 133 return false; 134 } 135 136 void CaptchaView::OnImageDecoded(const ImageDecoder*, 137 const SkBitmap& decoded_image) { 138 captcha_image_->SetImage(decoded_image); 139 captcha_textfield_->RequestFocus(); 140 Layout(); 141 } 142 143 void CaptchaView::ButtonPressed(views::Button* sender, 144 const views::Event& event) { 145 DCHECK(sender == ok_button_); 146 Accept(); 147 } 148 149 void CaptchaView::Init() { 150 if (is_standalone_) { 151 // Use rounded rect background. 152 set_border(CreateWizardBorder(&BorderDefinition::kUserBorder)); 153 views::Painter* painter = CreateWizardPainter( 154 &BorderDefinition::kUserBorder); 155 set_background(views::Background::CreateBackgroundPainter(true, painter)); 156 } 157 158 views::GridLayout* layout = views::GridLayout::CreatePanel(this); 159 SetLayoutManager(layout); 160 161 int column_view_set_id = 0; 162 views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id); 163 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, 164 views::GridLayout::USE_PREF, 0, 0); 165 layout->StartRow(0, column_view_set_id); 166 Label* label = new views::Label( 167 UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_CAPTCHA_INSTRUCTIONS))); 168 label->SetMultiLine(true); 169 layout->AddView(label); 170 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 171 172 layout->StartRow(0, column_view_set_id); 173 captcha_image_ = new views::ImageView(); 174 layout->AddView(captcha_image_); 175 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 176 177 layout->StartRow(0, column_view_set_id); 178 captcha_textfield_ = new CaptchaField(); 179 captcha_textfield_->SetController(this); 180 if (is_standalone_) 181 captcha_textfield_->set_background(new CopyBackground(this)); 182 layout->AddView(captcha_textfield_); 183 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 184 185 layout->StartRow(0, column_view_set_id); 186 label = new views::Label(UTF16ToWide( 187 l10n_util::GetStringUTF16(IDS_SYNC_GAIA_CAPTCHA_CASE_INSENSITIVE_TIP))); 188 label->SetMultiLine(true); 189 layout->AddView(label); 190 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 191 192 if (is_standalone_) { 193 layout->StartRow(0, column_view_set_id); 194 ok_button_ = new WideTextButton( 195 this, UTF16ToWide(l10n_util::GetStringUTF16(IDS_OK))); 196 ok_button_->set_alignment(views::TextButton::ALIGN_CENTER); 197 ok_button_->SetFocusable(true); 198 ok_button_->SetNormalHasBorder(true); 199 ok_button_->set_animate_on_state_change(false); 200 ok_button_->SetEnabledColor(SK_ColorBLACK); 201 ok_button_->SetHighlightColor(SK_ColorBLACK); 202 ok_button_->SetHoverColor(SK_ColorBLACK); 203 layout->AddView(ok_button_, 1, 1, 204 views::GridLayout::CENTER, views::GridLayout::CENTER); 205 } 206 207 // ImageDownloader will delete itself once URL is fetched. 208 // TODO(nkostylev): Make sure that it works after view is deleted. 209 new ImageDownloader(this, GURL(captcha_url_), std::string()); 210 } 211 212 } // namespace chromeos 213