1// Copyright (c) 2012 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 "ui/views/controls/textfield/textfield.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "ui/base/accessibility/accessible_view_state.h"
13#include "ui/base/ime/text_input_type.h"
14#include "ui/base/resource/resource_bundle.h"
15#include "ui/base/ui_base_switches.h"
16#include "ui/events/event.h"
17#include "ui/events/keycodes/keyboard_codes.h"
18#include "ui/gfx/insets.h"
19#include "ui/gfx/range/range.h"
20#include "ui/gfx/selection_model.h"
21#include "ui/native_theme/native_theme.h"
22#include "ui/views/controls/native/native_view_host.h"
23#include "ui/views/controls/textfield/native_textfield_views.h"
24#include "ui/views/controls/textfield/native_textfield_wrapper.h"
25#include "ui/views/controls/textfield/textfield_controller.h"
26#include "ui/views/painter.h"
27#include "ui/views/views_delegate.h"
28#include "ui/views/widget/widget.h"
29
30namespace {
31
32// Default placeholder text color.
33const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
34
35gfx::FontList GetDefaultFontList() {
36  return ResourceBundle::GetSharedInstance().GetFontList(
37      ResourceBundle::BaseFont);
38}
39
40}  // namespace
41
42namespace views {
43
44// static
45const char Textfield::kViewClassName[] = "Textfield";
46
47// static
48size_t Textfield::GetCaretBlinkMs() {
49  static const size_t default_value = 500;
50#if defined(OS_WIN)
51  static const size_t system_value = ::GetCaretBlinkTime();
52  if (system_value != 0)
53    return (system_value == INFINITE) ? 0 : system_value;
54#endif
55  return default_value;
56}
57
58Textfield::Textfield()
59    : native_wrapper_(NULL),
60      controller_(NULL),
61      style_(STYLE_DEFAULT),
62      font_list_(GetDefaultFontList()),
63      read_only_(false),
64      default_width_in_chars_(0),
65      draw_border_(true),
66      text_color_(SK_ColorBLACK),
67      use_default_text_color_(true),
68      background_color_(SK_ColorWHITE),
69      use_default_background_color_(true),
70      horizontal_margins_were_set_(false),
71      vertical_margins_were_set_(false),
72      placeholder_text_color_(kDefaultPlaceholderTextColor),
73      text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
74      weak_ptr_factory_(this) {
75  SetFocusable(true);
76
77  if (ViewsDelegate::views_delegate) {
78    obscured_reveal_duration_ = ViewsDelegate::views_delegate->
79        GetDefaultTextfieldObscuredRevealDuration();
80  }
81
82  if (NativeViewHost::kRenderNativeControlFocus)
83    focus_painter_ = Painter::CreateDashedFocusPainter();
84}
85
86Textfield::Textfield(StyleFlags style)
87    : native_wrapper_(NULL),
88      controller_(NULL),
89      style_(style),
90      font_list_(GetDefaultFontList()),
91      read_only_(false),
92      default_width_in_chars_(0),
93      draw_border_(true),
94      text_color_(SK_ColorBLACK),
95      use_default_text_color_(true),
96      background_color_(SK_ColorWHITE),
97      use_default_background_color_(true),
98      horizontal_margins_were_set_(false),
99      vertical_margins_were_set_(false),
100      placeholder_text_color_(kDefaultPlaceholderTextColor),
101      text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
102      weak_ptr_factory_(this) {
103  SetFocusable(true);
104  if (IsObscured())
105    SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
106
107  if (ViewsDelegate::views_delegate) {
108    obscured_reveal_duration_ = ViewsDelegate::views_delegate->
109        GetDefaultTextfieldObscuredRevealDuration();
110  }
111
112  if (NativeViewHost::kRenderNativeControlFocus)
113    focus_painter_ = Painter::CreateDashedFocusPainter();
114}
115
116Textfield::~Textfield() {
117}
118
119void Textfield::SetController(TextfieldController* controller) {
120  controller_ = controller;
121}
122
123TextfieldController* Textfield::GetController() const {
124  return controller_;
125}
126
127void Textfield::SetReadOnly(bool read_only) {
128  // Update read-only without changing the focusable state (or active, etc.).
129  read_only_ = read_only;
130  if (native_wrapper_) {
131    native_wrapper_->UpdateReadOnly();
132    native_wrapper_->UpdateTextColor();
133    native_wrapper_->UpdateBackgroundColor();
134  }
135}
136
137bool Textfield::IsObscured() const {
138  return style_ & STYLE_OBSCURED;
139}
140
141void Textfield::SetObscured(bool obscured) {
142  if (obscured) {
143    style_ = static_cast<StyleFlags>(style_ | STYLE_OBSCURED);
144    SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
145  } else {
146    style_ = static_cast<StyleFlags>(style_ & ~STYLE_OBSCURED);
147    SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
148  }
149  if (native_wrapper_)
150    native_wrapper_->UpdateIsObscured();
151}
152
153ui::TextInputType Textfield::GetTextInputType() const {
154  if (read_only() || !enabled())
155    return ui::TEXT_INPUT_TYPE_NONE;
156  return text_input_type_;
157}
158
159void Textfield::SetTextInputType(ui::TextInputType type) {
160  text_input_type_ = type;
161  bool should_be_obscured = type == ui::TEXT_INPUT_TYPE_PASSWORD;
162  if (IsObscured() != should_be_obscured)
163    SetObscured(should_be_obscured);
164}
165
166void Textfield::SetText(const string16& text) {
167  text_ = text;
168  if (native_wrapper_)
169    native_wrapper_->UpdateText();
170}
171
172void Textfield::AppendText(const string16& text) {
173  text_ += text;
174  if (native_wrapper_)
175    native_wrapper_->AppendText(text);
176}
177
178void Textfield::InsertOrReplaceText(const string16& text) {
179  if (native_wrapper_) {
180    native_wrapper_->InsertOrReplaceText(text);
181    text_ = native_wrapper_->GetText();
182  }
183}
184
185base::i18n::TextDirection Textfield::GetTextDirection() const {
186  return native_wrapper_ ?
187      native_wrapper_->GetTextDirection() : base::i18n::UNKNOWN_DIRECTION;
188}
189
190void Textfield::SelectAll(bool reversed) {
191  if (native_wrapper_)
192    native_wrapper_->SelectAll(reversed);
193}
194
195string16 Textfield::GetSelectedText() const {
196  return native_wrapper_ ? native_wrapper_->GetSelectedText() : string16();
197}
198
199void Textfield::ClearSelection() const {
200  if (native_wrapper_)
201    native_wrapper_->ClearSelection();
202}
203
204bool Textfield::HasSelection() const {
205  return native_wrapper_ && !native_wrapper_->GetSelectedRange().is_empty();
206}
207
208SkColor Textfield::GetTextColor() const {
209  if (!use_default_text_color_)
210    return text_color_;
211
212  return GetNativeTheme()->GetSystemColor(read_only() ?
213      ui::NativeTheme::kColorId_TextfieldReadOnlyColor :
214      ui::NativeTheme::kColorId_TextfieldDefaultColor);
215}
216
217void Textfield::SetTextColor(SkColor color) {
218  text_color_ = color;
219  use_default_text_color_ = false;
220  if (native_wrapper_)
221    native_wrapper_->UpdateTextColor();
222}
223
224void Textfield::UseDefaultTextColor() {
225  use_default_text_color_ = true;
226  if (native_wrapper_)
227    native_wrapper_->UpdateTextColor();
228}
229
230SkColor Textfield::GetBackgroundColor() const {
231  if (!use_default_background_color_)
232    return background_color_;
233
234  return GetNativeTheme()->GetSystemColor(read_only() ?
235      ui::NativeTheme::kColorId_TextfieldReadOnlyBackground :
236      ui::NativeTheme::kColorId_TextfieldDefaultBackground);
237}
238
239void Textfield::SetBackgroundColor(SkColor color) {
240  background_color_ = color;
241  use_default_background_color_ = false;
242  if (native_wrapper_)
243    native_wrapper_->UpdateBackgroundColor();
244}
245
246void Textfield::UseDefaultBackgroundColor() {
247  use_default_background_color_ = true;
248  if (native_wrapper_)
249    native_wrapper_->UpdateBackgroundColor();
250}
251
252bool Textfield::GetCursorEnabled() const {
253  return native_wrapper_ && native_wrapper_->GetCursorEnabled();
254}
255
256void Textfield::SetCursorEnabled(bool enabled) {
257  if (native_wrapper_)
258    native_wrapper_->SetCursorEnabled(enabled);
259}
260
261void Textfield::SetFontList(const gfx::FontList& font_list) {
262  font_list_ = font_list;
263  if (native_wrapper_)
264    native_wrapper_->UpdateFont();
265  PreferredSizeChanged();
266}
267
268const gfx::Font& Textfield::GetPrimaryFont() const {
269  return font_list_.GetPrimaryFont();
270}
271
272void Textfield::SetFont(const gfx::Font& font) {
273  SetFontList(gfx::FontList(font));
274}
275
276void Textfield::SetHorizontalMargins(int left, int right) {
277  if (horizontal_margins_were_set_ &&
278      left == margins_.left() && right == margins_.right()) {
279    return;
280  }
281  margins_.Set(margins_.top(), left, margins_.bottom(), right);
282  horizontal_margins_were_set_ = true;
283  if (native_wrapper_)
284    native_wrapper_->UpdateHorizontalMargins();
285  PreferredSizeChanged();
286}
287
288void Textfield::SetVerticalMargins(int top, int bottom) {
289  if (vertical_margins_were_set_ &&
290      top == margins_.top() && bottom == margins_.bottom()) {
291    return;
292  }
293  margins_.Set(top, margins_.left(), bottom, margins_.right());
294  vertical_margins_were_set_ = true;
295  if (native_wrapper_)
296    native_wrapper_->UpdateVerticalMargins();
297  PreferredSizeChanged();
298}
299
300void Textfield::RemoveBorder() {
301  if (!draw_border_)
302    return;
303
304  draw_border_ = false;
305  if (native_wrapper_)
306    native_wrapper_->UpdateBorder();
307}
308
309base::string16 Textfield::GetPlaceholderText() const {
310  return placeholder_text_;
311}
312
313bool Textfield::GetHorizontalMargins(int* left, int* right) {
314  if (!horizontal_margins_were_set_)
315    return false;
316
317  *left = margins_.left();
318  *right = margins_.right();
319  return true;
320}
321
322bool Textfield::GetVerticalMargins(int* top, int* bottom) {
323  if (!vertical_margins_were_set_)
324    return false;
325
326  *top = margins_.top();
327  *bottom = margins_.bottom();
328  return true;
329}
330
331void Textfield::UpdateAllProperties() {
332  if (native_wrapper_) {
333    native_wrapper_->UpdateText();
334    native_wrapper_->UpdateTextColor();
335    native_wrapper_->UpdateBackgroundColor();
336    native_wrapper_->UpdateReadOnly();
337    native_wrapper_->UpdateFont();
338    native_wrapper_->UpdateEnabled();
339    native_wrapper_->UpdateBorder();
340    native_wrapper_->UpdateIsObscured();
341    native_wrapper_->UpdateHorizontalMargins();
342    native_wrapper_->UpdateVerticalMargins();
343  }
344}
345
346void Textfield::SyncText() {
347  if (native_wrapper_) {
348    string16 new_text = native_wrapper_->GetText();
349    if (new_text != text_) {
350      text_ = new_text;
351      if (controller_)
352        controller_->ContentsChanged(this, text_);
353    }
354  }
355}
356
357bool Textfield::IsIMEComposing() const {
358  return native_wrapper_ && native_wrapper_->IsIMEComposing();
359}
360
361gfx::Range Textfield::GetSelectedRange() const {
362  return native_wrapper_->GetSelectedRange();
363}
364
365void Textfield::SelectRange(const gfx::Range& range) {
366  native_wrapper_->SelectRange(range);
367}
368
369gfx::SelectionModel Textfield::GetSelectionModel() const {
370  return native_wrapper_->GetSelectionModel();
371}
372
373void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
374  native_wrapper_->SelectSelectionModel(sel);
375}
376
377size_t Textfield::GetCursorPosition() const {
378  return native_wrapper_->GetCursorPosition();
379}
380
381void Textfield::SetColor(SkColor value) {
382  return native_wrapper_->SetColor(value);
383}
384
385void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
386  return native_wrapper_->ApplyColor(value, range);
387}
388
389void Textfield::SetStyle(gfx::TextStyle style, bool value) {
390  return native_wrapper_->SetStyle(style, value);
391}
392
393void Textfield::ApplyStyle(gfx::TextStyle style,
394                           bool value,
395                           const gfx::Range& range) {
396  return native_wrapper_->ApplyStyle(style, value, range);
397}
398
399void Textfield::ClearEditHistory() {
400  native_wrapper_->ClearEditHistory();
401}
402
403void Textfield::SetAccessibleName(const string16& name) {
404  accessible_name_ = name;
405}
406
407void Textfield::ExecuteCommand(int command_id) {
408  native_wrapper_->ExecuteTextCommand(command_id);
409}
410
411void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
412  focus_painter_ = focus_painter.Pass();
413}
414
415bool Textfield::HasTextBeingDragged() {
416  return native_wrapper_->HasTextBeingDragged();
417}
418
419////////////////////////////////////////////////////////////////////////////////
420// Textfield, View overrides:
421
422void Textfield::Layout() {
423  if (native_wrapper_) {
424    native_wrapper_->GetView()->SetBoundsRect(GetContentsBounds());
425    native_wrapper_->GetView()->Layout();
426  }
427}
428
429int Textfield::GetBaseline() const {
430  gfx::Insets insets = GetTextInsets();
431  const int baseline = native_wrapper_ ?
432      native_wrapper_->GetTextfieldBaseline() : font_list_.GetBaseline();
433  return insets.top() + baseline;
434}
435
436gfx::Size Textfield::GetPreferredSize() {
437  gfx::Insets insets = GetTextInsets();
438
439  const int font_height = native_wrapper_ ? native_wrapper_->GetFontHeight() :
440                                            font_list_.GetHeight();
441  return gfx::Size(
442      GetPrimaryFont().GetExpectedTextWidth(default_width_in_chars_)
443      + insets.width(),
444      font_height + insets.height());
445}
446
447void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
448  SelectAll(false);
449}
450
451bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
452  // Skip any accelerator handling of backspace; textfields handle this key.
453  // Also skip processing of [Alt]+<num-pad digit> Unicode alt key codes.
454  return e.key_code() == ui::VKEY_BACK || e.IsUnicodeKeyCode();
455}
456
457void Textfield::OnPaint(gfx::Canvas* canvas) {
458  View::OnPaint(canvas);
459  if (NativeViewHost::kRenderNativeControlFocus)
460    Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
461}
462
463bool Textfield::OnKeyPressed(const ui::KeyEvent& e) {
464  return native_wrapper_ && native_wrapper_->HandleKeyPressed(e);
465}
466
467bool Textfield::OnKeyReleased(const ui::KeyEvent& e) {
468  return native_wrapper_ && native_wrapper_->HandleKeyReleased(e);
469}
470
471bool Textfield::OnMouseDragged(const ui::MouseEvent& e) {
472  if (!e.IsOnlyRightMouseButton())
473    return View::OnMouseDragged(e);
474  return true;
475}
476
477void Textfield::OnFocus() {
478  if (native_wrapper_)
479    native_wrapper_->HandleFocus();
480
481  // Forward the focus to the wrapper if it exists.
482  if (!native_wrapper_ || !native_wrapper_->SetFocus()) {
483    // If there is no wrapper or the wrapper didn't take focus, call
484    // View::Focus to clear the native focus so that we still get
485    // keyboard messages.
486    View::OnFocus();
487  }
488
489  // Border typically draws focus indicator.
490  SchedulePaint();
491}
492
493void Textfield::OnBlur() {
494  if (native_wrapper_)
495    native_wrapper_->HandleBlur();
496
497  // Border typically draws focus indicator.
498  SchedulePaint();
499}
500
501void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
502  state->role = ui::AccessibilityTypes::ROLE_TEXT;
503  state->name = accessible_name_;
504  if (read_only())
505    state->state |= ui::AccessibilityTypes::STATE_READONLY;
506  if (IsObscured())
507    state->state |= ui::AccessibilityTypes::STATE_PROTECTED;
508  state->value = text_;
509
510  const gfx::Range range = native_wrapper_->GetSelectedRange();
511  state->selection_start = range.start();
512  state->selection_end = range.end();
513
514  if (!read_only()) {
515    state->set_value_callback =
516        base::Bind(&Textfield::AccessibilitySetValue,
517                   weak_ptr_factory_.GetWeakPtr());
518  }
519}
520
521ui::TextInputClient* Textfield::GetTextInputClient() {
522  return native_wrapper_ ? native_wrapper_->GetTextInputClient() : NULL;
523}
524
525gfx::Point Textfield::GetKeyboardContextMenuLocation() {
526  return native_wrapper_ ? native_wrapper_->GetContextMenuLocation() :
527                           View::GetKeyboardContextMenuLocation();
528}
529
530void Textfield::OnEnabledChanged() {
531  View::OnEnabledChanged();
532  if (native_wrapper_)
533    native_wrapper_->UpdateEnabled();
534}
535
536void Textfield::ViewHierarchyChanged(
537    const ViewHierarchyChangedDetails& details) {
538  if (details.is_add && !native_wrapper_ && GetWidget()) {
539    // The native wrapper's lifetime will be managed by the view hierarchy after
540    // we call AddChildView.
541    native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
542    AddChildViewAt(native_wrapper_->GetView(), 0);
543    Layout();
544    UpdateAllProperties();
545  }
546}
547
548const char* Textfield::GetClassName() const {
549  return kViewClassName;
550}
551
552////////////////////////////////////////////////////////////////////////////////
553// Textfield, private:
554
555gfx::Insets Textfield::GetTextInsets() const {
556  gfx::Insets insets = GetInsets();
557  if (draw_border_ && native_wrapper_)
558    insets += native_wrapper_->CalculateInsets();
559  return insets;
560}
561
562void Textfield::AccessibilitySetValue(const string16& new_value) {
563  if (!read_only()) {
564    SetText(new_value);
565    ClearSelection();
566  }
567}
568
569////////////////////////////////////////////////////////////////////////////////
570// NativeTextfieldWrapper, public:
571
572// static
573NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
574    Textfield* field) {
575  return new NativeTextfieldViews(field);
576}
577
578}  // namespace views
579