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:
CaptchaField()41 CaptchaField()
42 : TextfieldWithMargin() {
43 }
44
45 // views::View overrides.
OnMousePressed(const views::MouseEvent & e)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:
WideTextButton(views::ButtonListener * listener,const std::wstring & text)57 WideTextButton(views::ButtonListener* listener, const std::wstring& text)
58 : TextButton(listener, text) {
59 SetFont(font().DeriveFont(kFontSizeCorrectionDelta));
60 }
61
~WideTextButton()62 virtual ~WideTextButton() {}
63
64 private:
GetPreferredSize()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
CaptchaView(const GURL & captcha_url,bool is_standalone)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
Accept()87 bool CaptchaView::Accept() {
88 if (delegate_)
89 delegate_->OnCaptchaEntered(UTF16ToUTF8(captcha_textfield_->text()));
90 return true;
91 }
92
GetWindowTitle() const93 std::wstring CaptchaView::GetWindowTitle() const {
94 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_CAPTCHA_DIALOG_TITLE));
95 }
96
SetCaptchaURL(const GURL & captcha_url)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
GetPreferredSize()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
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)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
HandleKeyEvent(views::Textfield * sender,const views::KeyEvent & key_event)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
OnImageDecoded(const ImageDecoder *,const SkBitmap & decoded_image)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
ButtonPressed(views::Button * sender,const views::Event & event)143 void CaptchaView::ButtonPressed(views::Button* sender,
144 const views::Event& event) {
145 DCHECK(sender == ok_button_);
146 Accept();
147 }
148
Init()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