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