• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/debug/trace_event.h"
10 #include "grit/ui_strings.h"
11 #include "ui/accessibility/ax_view_state.h"
12 #include "ui/base/clipboard/scoped_clipboard_writer.h"
13 #include "ui/base/cursor/cursor.h"
14 #include "ui/base/dragdrop/drag_drop_types.h"
15 #include "ui/base/dragdrop/drag_utils.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
19 #include "ui/events/event.h"
20 #include "ui/events/keycodes/keyboard_codes.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/insets.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/native_theme/native_theme.h"
26 #include "ui/views/background.h"
27 #include "ui/views/controls/focusable_border.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/controls/menu/menu_runner.h"
30 #include "ui/views/controls/native/native_view_host.h"
31 #include "ui/views/controls/textfield/textfield_controller.h"
32 #include "ui/views/drag_utils.h"
33 #include "ui/views/ime/input_method.h"
34 #include "ui/views/metrics.h"
35 #include "ui/views/native_cursor.h"
36 #include "ui/views/painter.h"
37 #include "ui/views/views_delegate.h"
38 #include "ui/views/widget/widget.h"
39 
40 #if defined(OS_WIN)
41 #include "base/win/win_util.h"
42 #endif
43 
44 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
45 #include "base/strings/utf_string_conversions.h"
46 #include "ui/events/linux/text_edit_command_auralinux.h"
47 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
48 #endif
49 
50 namespace views {
51 
52 namespace {
53 
54 // Default placeholder text color.
55 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
56 
57 const int kNoCommand = 0;
58 
ConvertRectToScreen(const View * src,gfx::Rect * r)59 void ConvertRectToScreen(const View* src, gfx::Rect* r) {
60   DCHECK(src);
61 
62   gfx::Point new_origin = r->origin();
63   View::ConvertPointToScreen(src, &new_origin);
64   r->set_origin(new_origin);
65 }
66 
67 // Get the drag selection timer delay, respecting animation scaling for testing.
GetDragSelectionDelay()68 int GetDragSelectionDelay() {
69   switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) {
70       case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100;
71       case ui::ScopedAnimationDurationScaleMode::FAST_DURATION:   return 25;
72       case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION:   return 400;
73       case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION:   return 0;
74     }
75   return 100;
76 }
77 
78 // Get the default command for a given key |event| and selection state.
GetCommandForKeyEvent(const ui::KeyEvent & event,bool has_selection)79 int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) {
80   if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
81     return kNoCommand;
82 
83   const bool shift = event.IsShiftDown();
84   const bool control = event.IsControlDown();
85   const bool alt = event.IsAltDown() || event.IsAltGrDown();
86   switch (event.key_code()) {
87     case ui::VKEY_Z:
88       if (control && !shift && !alt)
89         return IDS_APP_UNDO;
90       return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand;
91     case ui::VKEY_Y:
92       return (control && !alt) ? IDS_APP_REDO : kNoCommand;
93     case ui::VKEY_A:
94       return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand;
95     case ui::VKEY_X:
96       return (control && !alt) ? IDS_APP_CUT : kNoCommand;
97     case ui::VKEY_C:
98       return (control && !alt) ? IDS_APP_COPY : kNoCommand;
99     case ui::VKEY_V:
100       return (control && !alt) ? IDS_APP_PASTE : kNoCommand;
101     case ui::VKEY_RIGHT:
102       // Ignore alt+right, which may be a browser navigation shortcut.
103       if (alt)
104         return kNoCommand;
105       if (!shift)
106         return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT;
107       return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
108                         IDS_MOVE_RIGHT_AND_MODIFY_SELECTION;
109     case ui::VKEY_LEFT:
110       // Ignore alt+left, which may be a browser navigation shortcut.
111       if (alt)
112         return kNoCommand;
113       if (!shift)
114         return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT;
115       return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
116                         IDS_MOVE_LEFT_AND_MODIFY_SELECTION;
117     case ui::VKEY_HOME:
118       return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
119                       IDS_MOVE_TO_BEGINNING_OF_LINE;
120     case ui::VKEY_END:
121       return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
122                       IDS_MOVE_TO_END_OF_LINE;
123     case ui::VKEY_BACK:
124       if (!control || has_selection)
125         return IDS_DELETE_BACKWARD;
126 #if defined(OS_LINUX)
127       // Only erase by line break on Linux and ChromeOS.
128       if (shift)
129         return IDS_DELETE_TO_BEGINNING_OF_LINE;
130 #endif
131       return IDS_DELETE_WORD_BACKWARD;
132     case ui::VKEY_DELETE:
133       if (!control || has_selection)
134         return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD;
135 #if defined(OS_LINUX)
136       // Only erase by line break on Linux and ChromeOS.
137       if (shift)
138         return IDS_DELETE_TO_END_OF_LINE;
139 #endif
140       return IDS_DELETE_WORD_FORWARD;
141     case ui::VKEY_INSERT:
142       if (control && !shift)
143         return IDS_APP_COPY;
144       return (shift && !control) ? IDS_APP_PASTE : kNoCommand;
145     default:
146       return kNoCommand;
147   }
148 }
149 
150 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
151 // Convert a custom text edit |command| to the equivalent views command ID.
GetViewsCommand(const ui::TextEditCommandAuraLinux & command,bool rtl)152 int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) {
153   const bool select = command.extend_selection();
154   switch (command.command_id()) {
155     case ui::TextEditCommandAuraLinux::COPY:
156       return IDS_APP_COPY;
157     case ui::TextEditCommandAuraLinux::CUT:
158       return IDS_APP_CUT;
159     case ui::TextEditCommandAuraLinux::DELETE_BACKWARD:
160       return IDS_DELETE_BACKWARD;
161     case ui::TextEditCommandAuraLinux::DELETE_FORWARD:
162       return IDS_DELETE_FORWARD;
163     case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE:
164     case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH:
165       return IDS_DELETE_TO_BEGINNING_OF_LINE;
166     case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE:
167     case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH:
168       return IDS_DELETE_TO_END_OF_LINE;
169     case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD:
170       return IDS_DELETE_WORD_BACKWARD;
171     case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD:
172       return IDS_DELETE_WORD_FORWARD;
173     case ui::TextEditCommandAuraLinux::INSERT_TEXT:
174       return kNoCommand;
175     case ui::TextEditCommandAuraLinux::MOVE_BACKWARD:
176       if (rtl)
177         return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
178       return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
179     case ui::TextEditCommandAuraLinux::MOVE_DOWN:
180       return IDS_MOVE_DOWN;
181     case ui::TextEditCommandAuraLinux::MOVE_FORWARD:
182       if (rtl)
183         return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
184       return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
185     case ui::TextEditCommandAuraLinux::MOVE_LEFT:
186       return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
187     case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN:
188     case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP:
189       return kNoCommand;
190     case ui::TextEditCommandAuraLinux::MOVE_RIGHT:
191       return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
192     case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT:
193     case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE:
194     case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH:
195       return select ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
196                       IDS_MOVE_TO_BEGINNING_OF_LINE;
197     case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT:
198     case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE:
199     case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH:
200       return select ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
201                       IDS_MOVE_TO_END_OF_LINE;
202     case ui::TextEditCommandAuraLinux::MOVE_UP:
203       return IDS_MOVE_UP;
204     case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD:
205       if (rtl) {
206         return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
207                         IDS_MOVE_WORD_RIGHT;
208       }
209       return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
210                       IDS_MOVE_WORD_LEFT;
211     case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD:
212       if (rtl) {
213         return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
214                         IDS_MOVE_WORD_LEFT;
215       }
216       return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
217                       IDS_MOVE_WORD_RIGHT;
218     case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT:
219       return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
220                       IDS_MOVE_WORD_LEFT;
221     case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT:
222       return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
223                       IDS_MOVE_WORD_RIGHT;
224     case ui::TextEditCommandAuraLinux::PASTE:
225       return IDS_APP_PASTE;
226     case ui::TextEditCommandAuraLinux::SELECT_ALL:
227       return IDS_APP_SELECT_ALL;
228     case ui::TextEditCommandAuraLinux::SET_MARK:
229     case ui::TextEditCommandAuraLinux::UNSELECT:
230     case ui::TextEditCommandAuraLinux::INVALID_COMMAND:
231       return kNoCommand;
232   }
233   return kNoCommand;
234 }
235 #endif
236 
237 }  // namespace
238 
239 // static
240 const char Textfield::kViewClassName[] = "Textfield";
241 
242 // static
GetCaretBlinkMs()243 size_t Textfield::GetCaretBlinkMs() {
244   static const size_t default_value = 500;
245 #if defined(OS_WIN)
246   static const size_t system_value = ::GetCaretBlinkTime();
247   if (system_value != 0)
248     return (system_value == INFINITE) ? 0 : system_value;
249 #endif
250   return default_value;
251 }
252 
Textfield()253 Textfield::Textfield()
254     : model_(new TextfieldModel(this)),
255       controller_(NULL),
256       read_only_(false),
257       default_width_in_chars_(0),
258       use_default_text_color_(true),
259       use_default_background_color_(true),
260       use_default_selection_text_color_(true),
261       use_default_selection_background_color_(true),
262       text_color_(SK_ColorBLACK),
263       background_color_(SK_ColorWHITE),
264       selection_text_color_(SK_ColorWHITE),
265       selection_background_color_(SK_ColorBLUE),
266       placeholder_text_color_(kDefaultPlaceholderTextColor),
267       text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
268       performing_user_action_(false),
269       skip_input_method_cancel_composition_(false),
270       cursor_visible_(false),
271       drop_cursor_visible_(false),
272       initiating_drag_(false),
273       aggregated_clicks_(0),
274       weak_ptr_factory_(this) {
275   set_context_menu_controller(this);
276   set_drag_controller(this);
277   SetBorder(scoped_ptr<Border>(new FocusableBorder()));
278   SetFocusable(true);
279 
280   if (ViewsDelegate::views_delegate) {
281     password_reveal_duration_ = ViewsDelegate::views_delegate->
282         GetDefaultTextfieldObscuredRevealDuration();
283   }
284 }
285 
~Textfield()286 Textfield::~Textfield() {}
287 
SetReadOnly(bool read_only)288 void Textfield::SetReadOnly(bool read_only) {
289   // Update read-only without changing the focusable state (or active, etc.).
290   read_only_ = read_only;
291   if (GetInputMethod())
292     GetInputMethod()->OnTextInputTypeChanged(this);
293   SetColor(GetTextColor());
294   UpdateBackgroundColor();
295 }
296 
SetTextInputType(ui::TextInputType type)297 void Textfield::SetTextInputType(ui::TextInputType type) {
298   GetRenderText()->SetObscured(type == ui::TEXT_INPUT_TYPE_PASSWORD);
299   text_input_type_ = type;
300   OnCaretBoundsChanged();
301   if (GetInputMethod())
302     GetInputMethod()->OnTextInputTypeChanged(this);
303   SchedulePaint();
304 }
305 
SetText(const base::string16 & new_text)306 void Textfield::SetText(const base::string16& new_text) {
307   model_->SetText(new_text);
308   OnCaretBoundsChanged();
309   SchedulePaint();
310   NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
311 }
312 
AppendText(const base::string16 & new_text)313 void Textfield::AppendText(const base::string16& new_text) {
314   if (new_text.empty())
315     return;
316   model_->Append(new_text);
317   OnCaretBoundsChanged();
318   SchedulePaint();
319 }
320 
InsertOrReplaceText(const base::string16 & new_text)321 void Textfield::InsertOrReplaceText(const base::string16& new_text) {
322   if (new_text.empty())
323     return;
324   model_->InsertText(new_text);
325   OnCaretBoundsChanged();
326   SchedulePaint();
327 }
328 
GetTextDirection() const329 base::i18n::TextDirection Textfield::GetTextDirection() const {
330   return GetRenderText()->GetTextDirection();
331 }
332 
GetSelectedText() const333 base::string16 Textfield::GetSelectedText() const {
334   return model_->GetSelectedText();
335 }
336 
SelectAll(bool reversed)337 void Textfield::SelectAll(bool reversed) {
338   model_->SelectAll(reversed);
339   UpdateSelectionClipboard();
340   UpdateAfterChange(false, true);
341 }
342 
ClearSelection()343 void Textfield::ClearSelection() {
344   model_->ClearSelection();
345   UpdateAfterChange(false, true);
346 }
347 
HasSelection() const348 bool Textfield::HasSelection() const {
349   return !GetSelectedRange().is_empty();
350 }
351 
GetTextColor() const352 SkColor Textfield::GetTextColor() const {
353   if (!use_default_text_color_)
354     return text_color_;
355 
356   return GetNativeTheme()->GetSystemColor(read_only() ?
357       ui::NativeTheme::kColorId_TextfieldReadOnlyColor :
358       ui::NativeTheme::kColorId_TextfieldDefaultColor);
359 }
360 
SetTextColor(SkColor color)361 void Textfield::SetTextColor(SkColor color) {
362   text_color_ = color;
363   use_default_text_color_ = false;
364   SetColor(color);
365 }
366 
UseDefaultTextColor()367 void Textfield::UseDefaultTextColor() {
368   use_default_text_color_ = true;
369   SetColor(GetTextColor());
370 }
371 
GetBackgroundColor() const372 SkColor Textfield::GetBackgroundColor() const {
373   if (!use_default_background_color_)
374     return background_color_;
375 
376   return GetNativeTheme()->GetSystemColor(read_only() ?
377       ui::NativeTheme::kColorId_TextfieldReadOnlyBackground :
378       ui::NativeTheme::kColorId_TextfieldDefaultBackground);
379 }
380 
SetBackgroundColor(SkColor color)381 void Textfield::SetBackgroundColor(SkColor color) {
382   background_color_ = color;
383   use_default_background_color_ = false;
384   UpdateBackgroundColor();
385 }
386 
UseDefaultBackgroundColor()387 void Textfield::UseDefaultBackgroundColor() {
388   use_default_background_color_ = true;
389   UpdateBackgroundColor();
390 }
391 
GetSelectionTextColor() const392 SkColor Textfield::GetSelectionTextColor() const {
393   return use_default_selection_text_color_ ?
394       GetNativeTheme()->GetSystemColor(
395           ui::NativeTheme::kColorId_TextfieldSelectionColor) :
396       selection_text_color_;
397 }
398 
SetSelectionTextColor(SkColor color)399 void Textfield::SetSelectionTextColor(SkColor color) {
400   selection_text_color_ = color;
401   use_default_selection_text_color_ = false;
402   GetRenderText()->set_selection_color(GetSelectionTextColor());
403   SchedulePaint();
404 }
405 
UseDefaultSelectionTextColor()406 void Textfield::UseDefaultSelectionTextColor() {
407   use_default_selection_text_color_ = true;
408   GetRenderText()->set_selection_color(GetSelectionTextColor());
409   SchedulePaint();
410 }
411 
GetSelectionBackgroundColor() const412 SkColor Textfield::GetSelectionBackgroundColor() const {
413   return use_default_selection_background_color_ ?
414       GetNativeTheme()->GetSystemColor(
415           ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused) :
416       selection_background_color_;
417 }
418 
SetSelectionBackgroundColor(SkColor color)419 void Textfield::SetSelectionBackgroundColor(SkColor color) {
420   selection_background_color_ = color;
421   use_default_selection_background_color_ = false;
422   GetRenderText()->set_selection_background_focused_color(
423       GetSelectionBackgroundColor());
424   SchedulePaint();
425 }
426 
UseDefaultSelectionBackgroundColor()427 void Textfield::UseDefaultSelectionBackgroundColor() {
428   use_default_selection_background_color_ = true;
429   GetRenderText()->set_selection_background_focused_color(
430       GetSelectionBackgroundColor());
431   SchedulePaint();
432 }
433 
GetCursorEnabled() const434 bool Textfield::GetCursorEnabled() const {
435   return GetRenderText()->cursor_enabled();
436 }
437 
SetCursorEnabled(bool enabled)438 void Textfield::SetCursorEnabled(bool enabled) {
439   GetRenderText()->SetCursorEnabled(enabled);
440 }
441 
GetFontList() const442 const gfx::FontList& Textfield::GetFontList() const {
443   return GetRenderText()->font_list();
444 }
445 
SetFontList(const gfx::FontList & font_list)446 void Textfield::SetFontList(const gfx::FontList& font_list) {
447   GetRenderText()->SetFontList(font_list);
448   OnCaretBoundsChanged();
449   PreferredSizeChanged();
450 }
451 
GetPlaceholderText() const452 base::string16 Textfield::GetPlaceholderText() const {
453   return placeholder_text_;
454 }
455 
GetHorizontalAlignment() const456 gfx::HorizontalAlignment Textfield::GetHorizontalAlignment() const {
457   return GetRenderText()->horizontal_alignment();
458 }
459 
SetHorizontalAlignment(gfx::HorizontalAlignment alignment)460 void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
461   GetRenderText()->SetHorizontalAlignment(alignment);
462 }
463 
ShowImeIfNeeded()464 void Textfield::ShowImeIfNeeded() {
465   if (enabled() && !read_only())
466     GetInputMethod()->ShowImeIfNeeded();
467 }
468 
IsIMEComposing() const469 bool Textfield::IsIMEComposing() const {
470   return model_->HasCompositionText();
471 }
472 
GetSelectedRange() const473 const gfx::Range& Textfield::GetSelectedRange() const {
474   return GetRenderText()->selection();
475 }
476 
SelectRange(const gfx::Range & range)477 void Textfield::SelectRange(const gfx::Range& range) {
478   model_->SelectRange(range);
479   UpdateAfterChange(false, true);
480 }
481 
GetSelectionModel() const482 const gfx::SelectionModel& Textfield::GetSelectionModel() const {
483   return GetRenderText()->selection_model();
484 }
485 
SelectSelectionModel(const gfx::SelectionModel & sel)486 void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
487   model_->SelectSelectionModel(sel);
488   UpdateAfterChange(false, true);
489 }
490 
GetCursorPosition() const491 size_t Textfield::GetCursorPosition() const {
492   return model_->GetCursorPosition();
493 }
494 
SetColor(SkColor value)495 void Textfield::SetColor(SkColor value) {
496   GetRenderText()->SetColor(value);
497   SchedulePaint();
498 }
499 
ApplyColor(SkColor value,const gfx::Range & range)500 void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
501   GetRenderText()->ApplyColor(value, range);
502   SchedulePaint();
503 }
504 
SetStyle(gfx::TextStyle style,bool value)505 void Textfield::SetStyle(gfx::TextStyle style, bool value) {
506   GetRenderText()->SetStyle(style, value);
507   SchedulePaint();
508 }
509 
ApplyStyle(gfx::TextStyle style,bool value,const gfx::Range & range)510 void Textfield::ApplyStyle(gfx::TextStyle style,
511                            bool value,
512                            const gfx::Range& range) {
513   GetRenderText()->ApplyStyle(style, value, range);
514   SchedulePaint();
515 }
516 
ClearEditHistory()517 void Textfield::ClearEditHistory() {
518   model_->ClearEditHistory();
519 }
520 
SetAccessibleName(const base::string16 & name)521 void Textfield::SetAccessibleName(const base::string16& name) {
522   accessible_name_ = name;
523 }
524 
ExecuteCommand(int command_id)525 void Textfield::ExecuteCommand(int command_id) {
526   ExecuteCommand(command_id, ui::EF_NONE);
527 }
528 
SetFocusPainter(scoped_ptr<Painter> focus_painter)529 void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
530   focus_painter_ = focus_painter.Pass();
531 }
532 
HasTextBeingDragged()533 bool Textfield::HasTextBeingDragged() {
534   return initiating_drag_;
535 }
536 
537 ////////////////////////////////////////////////////////////////////////////////
538 // Textfield, View overrides:
539 
GetBaseline() const540 int Textfield::GetBaseline() const {
541   return GetInsets().top() + GetRenderText()->GetBaseline();
542 }
543 
GetPreferredSize() const544 gfx::Size Textfield::GetPreferredSize() const {
545   const gfx::Insets& insets = GetInsets();
546   return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) +
547                    insets.width(), GetFontList().GetHeight() + insets.height());
548 }
549 
GetClassName() const550 const char* Textfield::GetClassName() const {
551   return kViewClassName;
552 }
553 
GetCursor(const ui::MouseEvent & event)554 gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) {
555   bool in_selection = GetRenderText()->IsPointInSelection(event.location());
556   bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
557   bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
558   return text_cursor ? GetNativeIBeamCursor() : gfx::kNullCursor;
559 }
560 
OnMousePressed(const ui::MouseEvent & event)561 bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
562   TrackMouseClicks(event);
563 
564   if (!controller_ || !controller_->HandleMouseEvent(this, event)) {
565     if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) {
566       RequestFocus();
567       ShowImeIfNeeded();
568     }
569 
570     if (event.IsOnlyLeftMouseButton()) {
571       OnBeforeUserAction();
572       initiating_drag_ = false;
573       switch (aggregated_clicks_) {
574         case 0:
575           if (GetRenderText()->IsPointInSelection(event.location()))
576             initiating_drag_ = true;
577           else
578             MoveCursorTo(event.location(), event.IsShiftDown());
579           break;
580         case 1:
581           model_->MoveCursorTo(event.location(), false);
582           model_->SelectWord();
583           UpdateAfterChange(false, true);
584           double_click_word_ = GetRenderText()->selection();
585           break;
586         case 2:
587           SelectAll(false);
588           break;
589         default:
590           NOTREACHED();
591       }
592       OnAfterUserAction();
593     }
594 
595 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
596     if (event.IsOnlyMiddleMouseButton()) {
597       if (GetRenderText()->IsPointInSelection(event.location())) {
598         OnBeforeUserAction();
599         ClearSelection();
600         ui::ScopedClipboardWriter(
601             ui::Clipboard::GetForCurrentThread(),
602             ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16());
603         OnAfterUserAction();
604       } else if(!read_only()) {
605         PasteSelectionClipboard(event);
606       }
607     }
608 #endif
609   }
610 
611   return true;
612 }
613 
OnMouseDragged(const ui::MouseEvent & event)614 bool Textfield::OnMouseDragged(const ui::MouseEvent& event) {
615   last_drag_location_ = event.location();
616 
617   // Don't adjust the cursor on a potential drag and drop, or if the mouse
618   // movement from the last mouse click does not exceed the drag threshold.
619   if (initiating_drag_ || !event.IsOnlyLeftMouseButton() ||
620       !ExceededDragThreshold(last_drag_location_ - last_click_location_)) {
621     return true;
622   }
623 
624   // A timer is used to continuously scroll while selecting beyond side edges.
625   if ((event.location().x() > 0 && event.location().x() < size().width()) ||
626       GetDragSelectionDelay() == 0) {
627     drag_selection_timer_.Stop();
628     SelectThroughLastDragLocation();
629   } else if (!drag_selection_timer_.IsRunning()) {
630     drag_selection_timer_.Start(
631         FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()),
632         this, &Textfield::SelectThroughLastDragLocation);
633   }
634 
635   return true;
636 }
637 
OnMouseReleased(const ui::MouseEvent & event)638 void Textfield::OnMouseReleased(const ui::MouseEvent& event) {
639   OnBeforeUserAction();
640   drag_selection_timer_.Stop();
641   // Cancel suspected drag initiations, the user was clicking in the selection.
642   if (initiating_drag_)
643     MoveCursorTo(event.location(), false);
644   initiating_drag_ = false;
645   UpdateSelectionClipboard();
646   OnAfterUserAction();
647 }
648 
OnKeyPressed(const ui::KeyEvent & event)649 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
650   bool handled = controller_ && controller_->HandleKeyEvent(this, event);
651 
652 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
653   ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
654       ui::GetTextEditKeyBindingsDelegate();
655   std::vector<ui::TextEditCommandAuraLinux> commands;
656   if (!handled && delegate && delegate->MatchEvent(event, &commands)) {
657     const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
658     for (size_t i = 0; i < commands.size(); ++i) {
659       const int command = GetViewsCommand(commands[i], rtl);
660       if (IsCommandIdEnabled(command)) {
661         ExecuteCommand(command);
662         handled = true;
663       }
664     }
665     return handled;
666   }
667 #endif
668 
669   const int command = GetCommandForKeyEvent(event, HasSelection());
670   if (!handled && IsCommandIdEnabled(command)) {
671     ExecuteCommand(command);
672     handled = true;
673   }
674   return handled;
675 }
676 
GetTextInputClient()677 ui::TextInputClient* Textfield::GetTextInputClient() {
678   return read_only_ ? NULL : this;
679 }
680 
OnGestureEvent(ui::GestureEvent * event)681 void Textfield::OnGestureEvent(ui::GestureEvent* event) {
682   switch (event->type()) {
683     case ui::ET_GESTURE_TAP_DOWN:
684       OnBeforeUserAction();
685       RequestFocus();
686       ShowImeIfNeeded();
687 
688       // We don't deselect if the point is in the selection
689       // because TAP_DOWN may turn into a LONG_PRESS.
690       if (!GetRenderText()->IsPointInSelection(event->location()))
691         MoveCursorTo(event->location(), false);
692       OnAfterUserAction();
693       event->SetHandled();
694       break;
695     case ui::ET_GESTURE_SCROLL_UPDATE:
696       OnBeforeUserAction();
697       MoveCursorTo(event->location(), true);
698       OnAfterUserAction();
699       event->SetHandled();
700       break;
701     case ui::ET_GESTURE_SCROLL_END:
702     case ui::ET_SCROLL_FLING_START:
703       CreateTouchSelectionControllerAndNotifyIt();
704       event->SetHandled();
705       break;
706     case ui::ET_GESTURE_TAP:
707       if (event->details().tap_count() == 1) {
708         CreateTouchSelectionControllerAndNotifyIt();
709       } else {
710         DestroyTouchSelection();
711         OnBeforeUserAction();
712         SelectAll(false);
713         OnAfterUserAction();
714         event->SetHandled();
715       }
716 #if defined(OS_WIN)
717       if (!read_only())
718         base::win::DisplayVirtualKeyboard();
719 #endif
720       break;
721     case ui::ET_GESTURE_LONG_PRESS:
722       // If long press happens outside selection, select word and show context
723       // menu (If touch selection is enabled, context menu is shown by the
724       // |touch_selection_controller_|, hence we mark the event handled.
725       // Otherwise, the regular context menu will be shown by views).
726       // If long press happens in selected text and touch drag drop is enabled,
727       // we will turn off touch selection (if one exists) and let views do drag
728       // drop.
729       if (!GetRenderText()->IsPointInSelection(event->location())) {
730         OnBeforeUserAction();
731         model_->SelectWord();
732         touch_selection_controller_.reset(
733             ui::TouchSelectionController::create(this));
734         UpdateAfterChange(false, true);
735         OnAfterUserAction();
736         if (touch_selection_controller_)
737           event->SetHandled();
738       } else if (switches::IsTouchDragDropEnabled()) {
739         initiating_drag_ = true;
740         DestroyTouchSelection();
741       } else {
742         if (!touch_selection_controller_)
743           CreateTouchSelectionControllerAndNotifyIt();
744         if (touch_selection_controller_)
745           event->SetHandled();
746       }
747       return;
748     case ui::ET_GESTURE_LONG_TAP:
749       if (!touch_selection_controller_)
750         CreateTouchSelectionControllerAndNotifyIt();
751 
752       // If touch selection is enabled, the context menu on long tap will be
753       // shown by the |touch_selection_controller_|, hence we mark the event
754       // handled so views does not try to show context menu on it.
755       if (touch_selection_controller_)
756         event->SetHandled();
757       break;
758     default:
759       return;
760   }
761 }
762 
AboutToRequestFocusFromTabTraversal(bool reverse)763 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
764   SelectAll(false);
765 }
766 
SkipDefaultKeyEventProcessing(const ui::KeyEvent & event)767 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
768 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
769   // Skip any accelerator handling that conflicts with custom keybindings.
770   ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
771       ui::GetTextEditKeyBindingsDelegate();
772   std::vector<ui::TextEditCommandAuraLinux> commands;
773   if (delegate && delegate->MatchEvent(event, &commands)) {
774     const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
775     for (size_t i = 0; i < commands.size(); ++i)
776       if (IsCommandIdEnabled(GetViewsCommand(commands[i], rtl)))
777         return true;
778   }
779 #endif
780 
781   // Skip backspace accelerator handling; editable textfields handle this key.
782   // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes.
783   const bool is_backspace = event.key_code() == ui::VKEY_BACK;
784   return (is_backspace && !read_only()) || event.IsUnicodeKeyCode();
785 }
786 
GetDropFormats(int * formats,std::set<OSExchangeData::CustomFormat> * custom_formats)787 bool Textfield::GetDropFormats(
788     int* formats,
789     std::set<OSExchangeData::CustomFormat>* custom_formats) {
790   if (!enabled() || read_only())
791     return false;
792   // TODO(msw): Can we support URL, FILENAME, etc.?
793   *formats = ui::OSExchangeData::STRING;
794   if (controller_)
795     controller_->AppendDropFormats(formats, custom_formats);
796   return true;
797 }
798 
CanDrop(const OSExchangeData & data)799 bool Textfield::CanDrop(const OSExchangeData& data) {
800   int formats;
801   std::set<OSExchangeData::CustomFormat> custom_formats;
802   GetDropFormats(&formats, &custom_formats);
803   return enabled() && !read_only() &&
804       data.HasAnyFormat(formats, custom_formats);
805 }
806 
OnDragUpdated(const ui::DropTargetEvent & event)807 int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) {
808   DCHECK(CanDrop(event.data()));
809   gfx::RenderText* render_text = GetRenderText();
810   const gfx::Range& selection = render_text->selection();
811   drop_cursor_position_ = render_text->FindCursorPosition(event.location());
812   bool in_selection = !selection.is_empty() &&
813       selection.Contains(gfx::Range(drop_cursor_position_.caret_pos()));
814   drop_cursor_visible_ = !in_selection;
815   // TODO(msw): Pan over text when the user drags to the visible text edge.
816   OnCaretBoundsChanged();
817   SchedulePaint();
818 
819   if (initiating_drag_) {
820     if (in_selection)
821       return ui::DragDropTypes::DRAG_NONE;
822     return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
823                                    ui::DragDropTypes::DRAG_MOVE;
824   }
825   return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
826 }
827 
OnDragExited()828 void Textfield::OnDragExited() {
829   drop_cursor_visible_ = false;
830   SchedulePaint();
831 }
832 
OnPerformDrop(const ui::DropTargetEvent & event)833 int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) {
834   DCHECK(CanDrop(event.data()));
835   drop_cursor_visible_ = false;
836 
837   if (controller_) {
838     int drag_operation = controller_->OnDrop(event.data());
839     if (drag_operation != ui::DragDropTypes::DRAG_NONE)
840       return drag_operation;
841   }
842 
843   gfx::RenderText* render_text = GetRenderText();
844   DCHECK(!initiating_drag_ ||
845          !render_text->IsPointInSelection(event.location()));
846   OnBeforeUserAction();
847   skip_input_method_cancel_composition_ = true;
848 
849   gfx::SelectionModel drop_destination_model =
850       render_text->FindCursorPosition(event.location());
851   base::string16 new_text;
852   event.data().GetString(&new_text);
853 
854   // Delete the current selection for a drag and drop within this view.
855   const bool move = initiating_drag_ && !event.IsControlDown() &&
856                     event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
857   if (move) {
858     // Adjust the drop destination if it is on or after the current selection.
859     size_t pos = drop_destination_model.caret_pos();
860     pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length();
861     model_->DeleteSelectionAndInsertTextAt(new_text, pos);
862   } else {
863     model_->MoveCursorTo(drop_destination_model);
864     // Drop always inserts text even if the textfield is not in insert mode.
865     model_->InsertText(new_text);
866   }
867   skip_input_method_cancel_composition_ = false;
868   UpdateAfterChange(true, true);
869   OnAfterUserAction();
870   return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
871 }
872 
OnDragDone()873 void Textfield::OnDragDone() {
874   initiating_drag_ = false;
875   drop_cursor_visible_ = false;
876 }
877 
GetAccessibleState(ui::AXViewState * state)878 void Textfield::GetAccessibleState(ui::AXViewState* state) {
879   state->role = ui::AX_ROLE_TEXT_FIELD;
880   state->name = accessible_name_;
881   if (read_only())
882     state->AddStateFlag(ui::AX_STATE_READ_ONLY);
883   if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
884     state->AddStateFlag(ui::AX_STATE_PROTECTED);
885   state->value = text();
886 
887   const gfx::Range range = GetSelectedRange();
888   state->selection_start = range.start();
889   state->selection_end = range.end();
890 
891   if (!read_only()) {
892     state->set_value_callback =
893         base::Bind(&Textfield::AccessibilitySetValue,
894                    weak_ptr_factory_.GetWeakPtr());
895   }
896 }
897 
OnBoundsChanged(const gfx::Rect & previous_bounds)898 void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) {
899   GetRenderText()->SetDisplayRect(GetContentsBounds());
900   OnCaretBoundsChanged();
901 }
902 
OnEnabledChanged()903 void Textfield::OnEnabledChanged() {
904   View::OnEnabledChanged();
905   if (GetInputMethod())
906     GetInputMethod()->OnTextInputTypeChanged(this);
907   SchedulePaint();
908 }
909 
OnPaint(gfx::Canvas * canvas)910 void Textfield::OnPaint(gfx::Canvas* canvas) {
911   OnPaintBackground(canvas);
912   PaintTextAndCursor(canvas);
913   OnPaintBorder(canvas);
914 }
915 
OnFocus()916 void Textfield::OnFocus() {
917   GetRenderText()->set_focused(true);
918   cursor_visible_ = true;
919   SchedulePaint();
920   GetInputMethod()->OnFocus();
921   OnCaretBoundsChanged();
922 
923   const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
924   if (caret_blink_ms != 0) {
925     cursor_repaint_timer_.Start(FROM_HERE,
926         base::TimeDelta::FromMilliseconds(caret_blink_ms), this,
927         &Textfield::UpdateCursor);
928   }
929 
930   View::OnFocus();
931   SchedulePaint();
932 }
933 
OnBlur()934 void Textfield::OnBlur() {
935   GetRenderText()->set_focused(false);
936   GetInputMethod()->OnBlur();
937   cursor_repaint_timer_.Stop();
938   if (cursor_visible_) {
939     cursor_visible_ = false;
940     RepaintCursor();
941   }
942 
943   DestroyTouchSelection();
944 
945   // Border typically draws focus indicator.
946   SchedulePaint();
947 }
948 
GetKeyboardContextMenuLocation()949 gfx::Point Textfield::GetKeyboardContextMenuLocation() {
950   return GetCaretBounds().bottom_right();
951 }
952 
OnNativeThemeChanged(const ui::NativeTheme * theme)953 void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) {
954   gfx::RenderText* render_text = GetRenderText();
955   render_text->SetColor(GetTextColor());
956   UpdateBackgroundColor();
957   render_text->set_cursor_color(GetTextColor());
958   render_text->set_selection_color(GetSelectionTextColor());
959   render_text->set_selection_background_focused_color(
960       GetSelectionBackgroundColor());
961 }
962 
963 ////////////////////////////////////////////////////////////////////////////////
964 // Textfield, TextfieldModel::Delegate overrides:
965 
OnCompositionTextConfirmedOrCleared()966 void Textfield::OnCompositionTextConfirmedOrCleared() {
967   if (!skip_input_method_cancel_composition_)
968     GetInputMethod()->CancelComposition(this);
969 }
970 
971 ////////////////////////////////////////////////////////////////////////////////
972 // Textfield, ContextMenuController overrides:
973 
ShowContextMenuForView(View * source,const gfx::Point & point,ui::MenuSourceType source_type)974 void Textfield::ShowContextMenuForView(View* source,
975                                        const gfx::Point& point,
976                                        ui::MenuSourceType source_type) {
977   UpdateContextMenu();
978   ignore_result(context_menu_runner_->RunMenuAt(
979       GetWidget(),
980       NULL,
981       gfx::Rect(point, gfx::Size()),
982       MENU_ANCHOR_TOPLEFT,
983       source_type,
984       MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU));
985 }
986 
987 ////////////////////////////////////////////////////////////////////////////////
988 // Textfield, DragController overrides:
989 
WriteDragDataForView(View * sender,const gfx::Point & press_pt,OSExchangeData * data)990 void Textfield::WriteDragDataForView(View* sender,
991                                      const gfx::Point& press_pt,
992                                      OSExchangeData* data) {
993   const base::string16& selected_text(GetSelectedText());
994   data->SetString(selected_text);
995   Label label(selected_text, GetFontList());
996   label.SetBackgroundColor(GetBackgroundColor());
997   label.set_subpixel_rendering_enabled(false);
998   gfx::Size size(label.GetPreferredSize());
999   gfx::NativeView native_view = GetWidget()->GetNativeView();
1000   gfx::Display display = gfx::Screen::GetScreenFor(native_view)->
1001       GetDisplayNearestWindow(native_view);
1002   size.SetToMin(gfx::Size(display.size().width(), height()));
1003   label.SetBoundsRect(gfx::Rect(size));
1004   scoped_ptr<gfx::Canvas> canvas(
1005       GetCanvasForDragImage(GetWidget(), label.size()));
1006   label.SetEnabledColor(GetTextColor());
1007 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1008   // Desktop Linux Aura does not yet support transparency in drag images.
1009   canvas->DrawColor(GetBackgroundColor());
1010 #endif
1011   label.Paint(canvas.get(), views::CullSet());
1012   const gfx::Vector2d kOffset(-15, 0);
1013   drag_utils::SetDragImageOnDataObject(*canvas, kOffset, data);
1014   if (controller_)
1015     controller_->OnWriteDragData(data);
1016 }
1017 
GetDragOperationsForView(View * sender,const gfx::Point & p)1018 int Textfield::GetDragOperationsForView(View* sender, const gfx::Point& p) {
1019   int drag_operations = ui::DragDropTypes::DRAG_COPY;
1020   if (!enabled() || text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD ||
1021       !GetRenderText()->IsPointInSelection(p)) {
1022     drag_operations = ui::DragDropTypes::DRAG_NONE;
1023   } else if (sender == this && !read_only()) {
1024     drag_operations =
1025         ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
1026   }
1027   if (controller_)
1028     controller_->OnGetDragOperationsForTextfield(&drag_operations);
1029   return drag_operations;
1030 }
1031 
CanStartDragForView(View * sender,const gfx::Point & press_pt,const gfx::Point & p)1032 bool Textfield::CanStartDragForView(View* sender,
1033                                     const gfx::Point& press_pt,
1034                                     const gfx::Point& p) {
1035   return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt);
1036 }
1037 
1038 ////////////////////////////////////////////////////////////////////////////////
1039 // Textfield, ui::TouchEditable overrides:
1040 
SelectRect(const gfx::Point & start,const gfx::Point & end)1041 void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) {
1042   if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1043     return;
1044 
1045   gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start);
1046   gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end);
1047   gfx::SelectionModel selection(
1048       gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()),
1049       end_caret.caret_affinity());
1050 
1051   OnBeforeUserAction();
1052   SelectSelectionModel(selection);
1053   OnAfterUserAction();
1054 }
1055 
MoveCaretTo(const gfx::Point & point)1056 void Textfield::MoveCaretTo(const gfx::Point& point) {
1057   SelectRect(point, point);
1058 }
1059 
GetSelectionEndPoints(gfx::Rect * p1,gfx::Rect * p2)1060 void Textfield::GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) {
1061   gfx::RenderText* render_text = GetRenderText();
1062   const gfx::SelectionModel& sel = render_text->selection_model();
1063   gfx::SelectionModel start_sel =
1064       render_text->GetSelectionModelForSelectionStart();
1065   *p1 = render_text->GetCursorBounds(start_sel, true);
1066   *p2 = render_text->GetCursorBounds(sel, true);
1067 }
1068 
GetBounds()1069 gfx::Rect Textfield::GetBounds() {
1070   return GetLocalBounds();
1071 }
1072 
GetNativeView() const1073 gfx::NativeView Textfield::GetNativeView() const {
1074   return GetWidget()->GetNativeView();
1075 }
1076 
ConvertPointToScreen(gfx::Point * point)1077 void Textfield::ConvertPointToScreen(gfx::Point* point) {
1078   View::ConvertPointToScreen(this, point);
1079 }
1080 
ConvertPointFromScreen(gfx::Point * point)1081 void Textfield::ConvertPointFromScreen(gfx::Point* point) {
1082   View::ConvertPointFromScreen(this, point);
1083 }
1084 
DrawsHandles()1085 bool Textfield::DrawsHandles() {
1086   return false;
1087 }
1088 
OpenContextMenu(const gfx::Point & anchor)1089 void Textfield::OpenContextMenu(const gfx::Point& anchor) {
1090   DestroyTouchSelection();
1091   ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU);
1092 }
1093 
DestroyTouchSelection()1094 void Textfield::DestroyTouchSelection() {
1095   touch_selection_controller_.reset();
1096 }
1097 
1098 ////////////////////////////////////////////////////////////////////////////////
1099 // Textfield, ui::SimpleMenuModel::Delegate overrides:
1100 
IsCommandIdChecked(int command_id) const1101 bool Textfield::IsCommandIdChecked(int command_id) const {
1102   return true;
1103 }
1104 
IsCommandIdEnabled(int command_id) const1105 bool Textfield::IsCommandIdEnabled(int command_id) const {
1106   base::string16 result;
1107   bool editable = !read_only();
1108   bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
1109   switch (command_id) {
1110     case IDS_APP_UNDO:
1111       return editable && model_->CanUndo();
1112     case IDS_APP_REDO:
1113       return editable && model_->CanRedo();
1114     case IDS_APP_CUT:
1115       return editable && readable && model_->HasSelection();
1116     case IDS_APP_COPY:
1117       return readable && model_->HasSelection();
1118     case IDS_APP_PASTE:
1119       ui::Clipboard::GetForCurrentThread()->ReadText(
1120           ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
1121       return editable && !result.empty();
1122     case IDS_APP_DELETE:
1123       return editable && model_->HasSelection();
1124     case IDS_APP_SELECT_ALL:
1125       return !text().empty();
1126     case IDS_DELETE_FORWARD:
1127     case IDS_DELETE_BACKWARD:
1128     case IDS_DELETE_TO_BEGINNING_OF_LINE:
1129     case IDS_DELETE_TO_END_OF_LINE:
1130     case IDS_DELETE_WORD_BACKWARD:
1131     case IDS_DELETE_WORD_FORWARD:
1132       return editable;
1133     case IDS_MOVE_LEFT:
1134     case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1135     case IDS_MOVE_RIGHT:
1136     case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1137     case IDS_MOVE_WORD_LEFT:
1138     case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1139     case IDS_MOVE_WORD_RIGHT:
1140     case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1141     case IDS_MOVE_TO_BEGINNING_OF_LINE:
1142     case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1143     case IDS_MOVE_TO_END_OF_LINE:
1144     case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1145       return true;
1146     default:
1147       return false;
1148   }
1149 }
1150 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)1151 bool Textfield::GetAcceleratorForCommandId(int command_id,
1152                                            ui::Accelerator* accelerator) {
1153   return false;
1154 }
1155 
ExecuteCommand(int command_id,int event_flags)1156 void Textfield::ExecuteCommand(int command_id, int event_flags) {
1157   DestroyTouchSelection();
1158   if (!IsCommandIdEnabled(command_id))
1159     return;
1160 
1161   bool text_changed = false;
1162   bool cursor_changed = false;
1163   bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
1164   gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
1165   gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
1166   gfx::SelectionModel selection_model = GetSelectionModel();
1167 
1168   OnBeforeUserAction();
1169   switch (command_id) {
1170     case IDS_APP_UNDO:
1171       text_changed = cursor_changed = model_->Undo();
1172       break;
1173     case IDS_APP_REDO:
1174       text_changed = cursor_changed = model_->Redo();
1175       break;
1176     case IDS_APP_CUT:
1177       text_changed = cursor_changed = Cut();
1178       break;
1179     case IDS_APP_COPY:
1180       Copy();
1181       break;
1182     case IDS_APP_PASTE:
1183       text_changed = cursor_changed = Paste();
1184       break;
1185     case IDS_APP_DELETE:
1186       text_changed = cursor_changed = model_->Delete();
1187       break;
1188     case IDS_APP_SELECT_ALL:
1189       SelectAll(false);
1190       break;
1191     case IDS_DELETE_BACKWARD:
1192       text_changed = cursor_changed = model_->Backspace();
1193       break;
1194     case IDS_DELETE_FORWARD:
1195       text_changed = cursor_changed = model_->Delete();
1196       break;
1197     case IDS_DELETE_TO_END_OF_LINE:
1198       model_->MoveCursor(gfx::LINE_BREAK, end, true);
1199       text_changed = cursor_changed = model_->Delete();
1200       break;
1201     case IDS_DELETE_TO_BEGINNING_OF_LINE:
1202       model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1203       text_changed = cursor_changed = model_->Backspace();
1204       break;
1205     case IDS_DELETE_WORD_BACKWARD:
1206       model_->MoveCursor(gfx::WORD_BREAK, begin, true);
1207       text_changed = cursor_changed = model_->Backspace();
1208       break;
1209     case IDS_DELETE_WORD_FORWARD:
1210       model_->MoveCursor(gfx::WORD_BREAK, end, true);
1211       text_changed = cursor_changed = model_->Delete();
1212       break;
1213     case IDS_MOVE_LEFT:
1214       model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
1215       break;
1216     case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1217       model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
1218       break;
1219     case IDS_MOVE_RIGHT:
1220       model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
1221       break;
1222     case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1223       model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
1224       break;
1225     case IDS_MOVE_WORD_LEFT:
1226       model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
1227       break;
1228     case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1229       model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
1230       break;
1231     case IDS_MOVE_WORD_RIGHT:
1232       model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
1233       break;
1234     case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1235       model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
1236       break;
1237     case IDS_MOVE_TO_BEGINNING_OF_LINE:
1238       model_->MoveCursor(gfx::LINE_BREAK, begin, false);
1239       break;
1240     case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1241       model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1242       break;
1243     case IDS_MOVE_TO_END_OF_LINE:
1244       model_->MoveCursor(gfx::LINE_BREAK, end, false);
1245       break;
1246     case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1247       model_->MoveCursor(gfx::LINE_BREAK, end, true);
1248       break;
1249     default:
1250       NOTREACHED();
1251       break;
1252   }
1253 
1254   cursor_changed |= GetSelectionModel() != selection_model;
1255   if (cursor_changed)
1256     UpdateSelectionClipboard();
1257   UpdateAfterChange(text_changed, cursor_changed);
1258   OnAfterUserAction();
1259 }
1260 
1261 ////////////////////////////////////////////////////////////////////////////////
1262 // Textfield, ui::TextInputClient overrides:
1263 
SetCompositionText(const ui::CompositionText & composition)1264 void Textfield::SetCompositionText(const ui::CompositionText& composition) {
1265   if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1266     return;
1267 
1268   OnBeforeUserAction();
1269   skip_input_method_cancel_composition_ = true;
1270   model_->SetCompositionText(composition);
1271   skip_input_method_cancel_composition_ = false;
1272   UpdateAfterChange(true, true);
1273   OnAfterUserAction();
1274 }
1275 
ConfirmCompositionText()1276 void Textfield::ConfirmCompositionText() {
1277   if (!model_->HasCompositionText())
1278     return;
1279 
1280   OnBeforeUserAction();
1281   skip_input_method_cancel_composition_ = true;
1282   model_->ConfirmCompositionText();
1283   skip_input_method_cancel_composition_ = false;
1284   UpdateAfterChange(true, true);
1285   OnAfterUserAction();
1286 }
1287 
ClearCompositionText()1288 void Textfield::ClearCompositionText() {
1289   if (!model_->HasCompositionText())
1290     return;
1291 
1292   OnBeforeUserAction();
1293   skip_input_method_cancel_composition_ = true;
1294   model_->CancelCompositionText();
1295   skip_input_method_cancel_composition_ = false;
1296   UpdateAfterChange(true, true);
1297   OnAfterUserAction();
1298 }
1299 
InsertText(const base::string16 & new_text)1300 void Textfield::InsertText(const base::string16& new_text) {
1301   // TODO(suzhe): Filter invalid characters.
1302   if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty())
1303     return;
1304 
1305   OnBeforeUserAction();
1306   skip_input_method_cancel_composition_ = true;
1307   if (GetRenderText()->insert_mode())
1308     model_->InsertText(new_text);
1309   else
1310     model_->ReplaceText(new_text);
1311   skip_input_method_cancel_composition_ = false;
1312   UpdateAfterChange(true, true);
1313   OnAfterUserAction();
1314 }
1315 
InsertChar(base::char16 ch,int flags)1316 void Textfield::InsertChar(base::char16 ch, int flags) {
1317   const int kControlModifierMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
1318                                    ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN |
1319                                    ui::EF_MOD3_DOWN;
1320 
1321   // Filter out all control characters, including tab and new line characters,
1322   // and all characters with Alt modifier. But allow characters with the AltGr
1323   // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a
1324   // different flag that we don't care about.
1325   const bool should_insert_char =
1326       ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1327       (flags & kControlModifierMask) != ui::EF_ALT_DOWN;
1328   if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char)
1329     return;
1330 
1331   OnBeforeUserAction();
1332   skip_input_method_cancel_composition_ = true;
1333   if (GetRenderText()->insert_mode())
1334     model_->InsertChar(ch);
1335   else
1336     model_->ReplaceChar(ch);
1337   skip_input_method_cancel_composition_ = false;
1338 
1339   UpdateAfterChange(true, true);
1340   OnAfterUserAction();
1341 
1342   if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD &&
1343       password_reveal_duration_ != base::TimeDelta()) {
1344     const size_t change_offset = model_->GetCursorPosition();
1345     DCHECK_GT(change_offset, 0u);
1346     RevealPasswordChar(change_offset - 1);
1347   }
1348 }
1349 
GetAttachedWindow() const1350 gfx::NativeWindow Textfield::GetAttachedWindow() const {
1351   // Imagine the following hierarchy.
1352   //   [NativeWidget A] - FocusManager
1353   //     [View]
1354   //     [NativeWidget B]
1355   //       [View]
1356   //         [View X]
1357   // An important thing is that [NativeWidget A] owns Win32 input focus even
1358   // when [View X] is logically focused by FocusManager. As a result, an Win32
1359   // IME may want to interact with the native view of [NativeWidget A] rather
1360   // than that of [NativeWidget B]. This is why we need to call
1361   // GetTopLevelWidget() here.
1362   return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
1363 }
1364 
GetTextInputType() const1365 ui::TextInputType Textfield::GetTextInputType() const {
1366   if (read_only() || !enabled())
1367     return ui::TEXT_INPUT_TYPE_NONE;
1368   return text_input_type_;
1369 }
1370 
GetTextInputMode() const1371 ui::TextInputMode Textfield::GetTextInputMode() const {
1372   return ui::TEXT_INPUT_MODE_DEFAULT;
1373 }
1374 
CanComposeInline() const1375 bool Textfield::CanComposeInline() const {
1376   return true;
1377 }
1378 
GetCaretBounds() const1379 gfx::Rect Textfield::GetCaretBounds() const {
1380   gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds();
1381   ConvertRectToScreen(this, &rect);
1382   return rect;
1383 }
1384 
GetCompositionCharacterBounds(uint32 index,gfx::Rect * rect) const1385 bool Textfield::GetCompositionCharacterBounds(uint32 index,
1386                                               gfx::Rect* rect) const {
1387   DCHECK(rect);
1388   if (!HasCompositionText())
1389     return false;
1390   gfx::RenderText* render_text = GetRenderText();
1391   const gfx::Range& composition_range = render_text->GetCompositionRange();
1392   DCHECK(!composition_range.is_empty());
1393 
1394   size_t text_index = composition_range.start() + index;
1395   if (composition_range.end() <= text_index)
1396     return false;
1397   if (!render_text->IsValidCursorIndex(text_index)) {
1398     text_index = render_text->IndexOfAdjacentGrapheme(
1399         text_index, gfx::CURSOR_BACKWARD);
1400   }
1401   if (text_index < composition_range.start())
1402     return false;
1403   const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD);
1404   *rect = render_text->GetCursorBounds(caret, false);
1405   ConvertRectToScreen(this, rect);
1406   return true;
1407 }
1408 
HasCompositionText() const1409 bool Textfield::HasCompositionText() const {
1410   return model_->HasCompositionText();
1411 }
1412 
GetTextRange(gfx::Range * range) const1413 bool Textfield::GetTextRange(gfx::Range* range) const {
1414   if (!ImeEditingAllowed())
1415     return false;
1416 
1417   model_->GetTextRange(range);
1418   return true;
1419 }
1420 
GetCompositionTextRange(gfx::Range * range) const1421 bool Textfield::GetCompositionTextRange(gfx::Range* range) const {
1422   if (!ImeEditingAllowed())
1423     return false;
1424 
1425   model_->GetCompositionTextRange(range);
1426   return true;
1427 }
1428 
GetSelectionRange(gfx::Range * range) const1429 bool Textfield::GetSelectionRange(gfx::Range* range) const {
1430   if (!ImeEditingAllowed())
1431     return false;
1432   *range = GetRenderText()->selection();
1433   return true;
1434 }
1435 
SetSelectionRange(const gfx::Range & range)1436 bool Textfield::SetSelectionRange(const gfx::Range& range) {
1437   if (!ImeEditingAllowed() || !range.IsValid())
1438     return false;
1439   OnBeforeUserAction();
1440   SelectRange(range);
1441   OnAfterUserAction();
1442   return true;
1443 }
1444 
DeleteRange(const gfx::Range & range)1445 bool Textfield::DeleteRange(const gfx::Range& range) {
1446   if (!ImeEditingAllowed() || range.is_empty())
1447     return false;
1448 
1449   OnBeforeUserAction();
1450   model_->SelectRange(range);
1451   if (model_->HasSelection()) {
1452     model_->DeleteSelection();
1453     UpdateAfterChange(true, true);
1454   }
1455   OnAfterUserAction();
1456   return true;
1457 }
1458 
GetTextFromRange(const gfx::Range & range,base::string16 * range_text) const1459 bool Textfield::GetTextFromRange(const gfx::Range& range,
1460                                  base::string16* range_text) const {
1461   if (!ImeEditingAllowed() || !range.IsValid())
1462     return false;
1463 
1464   gfx::Range text_range;
1465   if (!GetTextRange(&text_range) || !text_range.Contains(range))
1466     return false;
1467 
1468   *range_text = model_->GetTextFromRange(range);
1469   return true;
1470 }
1471 
OnInputMethodChanged()1472 void Textfield::OnInputMethodChanged() {}
1473 
ChangeTextDirectionAndLayoutAlignment(base::i18n::TextDirection direction)1474 bool Textfield::ChangeTextDirectionAndLayoutAlignment(
1475     base::i18n::TextDirection direction) {
1476   // Restore text directionality mode when the indicated direction matches the
1477   // current forced mode; otherwise, force the mode indicated. This helps users
1478   // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1479   const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ?
1480       gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
1481   if (mode == GetRenderText()->directionality_mode())
1482     GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
1483   else
1484     GetRenderText()->SetDirectionalityMode(mode);
1485   SchedulePaint();
1486   return true;
1487 }
1488 
ExtendSelectionAndDelete(size_t before,size_t after)1489 void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) {
1490   gfx::Range range = GetRenderText()->selection();
1491   DCHECK_GE(range.start(), before);
1492 
1493   range.set_start(range.start() - before);
1494   range.set_end(range.end() + after);
1495   gfx::Range text_range;
1496   if (GetTextRange(&text_range) && text_range.Contains(range))
1497     DeleteRange(range);
1498 }
1499 
EnsureCaretInRect(const gfx::Rect & rect)1500 void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {}
1501 
OnCandidateWindowShown()1502 void Textfield::OnCandidateWindowShown() {}
1503 
OnCandidateWindowUpdated()1504 void Textfield::OnCandidateWindowUpdated() {}
1505 
OnCandidateWindowHidden()1506 void Textfield::OnCandidateWindowHidden() {}
1507 
IsEditingCommandEnabled(int command_id)1508 bool Textfield::IsEditingCommandEnabled(int command_id) {
1509   return IsCommandIdEnabled(command_id);
1510 }
1511 
ExecuteEditingCommand(int command_id)1512 void Textfield::ExecuteEditingCommand(int command_id) {
1513   ExecuteCommand(command_id);
1514 }
1515 
1516 ////////////////////////////////////////////////////////////////////////////////
1517 // Textfield, protected:
1518 
GetRenderText() const1519 gfx::RenderText* Textfield::GetRenderText() const {
1520   return model_->render_text();
1521 }
1522 
GetSelectionClipboardText() const1523 base::string16 Textfield::GetSelectionClipboardText() const {
1524   base::string16 selection_clipboard_text;
1525   ui::Clipboard::GetForCurrentThread()->ReadText(
1526       ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text);
1527   return selection_clipboard_text;
1528 }
1529 
1530 ////////////////////////////////////////////////////////////////////////////////
1531 // Textfield, private:
1532 
AccessibilitySetValue(const base::string16 & new_value)1533 void Textfield::AccessibilitySetValue(const base::string16& new_value) {
1534   if (!read_only()) {
1535     SetText(new_value);
1536     ClearSelection();
1537   }
1538 }
1539 
UpdateBackgroundColor()1540 void Textfield::UpdateBackgroundColor() {
1541   const SkColor color = GetBackgroundColor();
1542   set_background(Background::CreateSolidBackground(color));
1543   GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF);
1544   SchedulePaint();
1545 }
1546 
UpdateAfterChange(bool text_changed,bool cursor_changed)1547 void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) {
1548   if (text_changed) {
1549     if (controller_)
1550       controller_->ContentsChanged(this, text());
1551     NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
1552   }
1553   if (cursor_changed) {
1554     cursor_visible_ = true;
1555     RepaintCursor();
1556     if (cursor_repaint_timer_.IsRunning())
1557       cursor_repaint_timer_.Reset();
1558     if (!text_changed) {
1559       // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire
1560       // this if only the selection changed.
1561       NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION_CHANGED, true);
1562     }
1563   }
1564   if (text_changed || cursor_changed) {
1565     OnCaretBoundsChanged();
1566     SchedulePaint();
1567   }
1568 }
1569 
UpdateCursor()1570 void Textfield::UpdateCursor() {
1571   const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1572   cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0);
1573   RepaintCursor();
1574 }
1575 
RepaintCursor()1576 void Textfield::RepaintCursor() {
1577   gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds());
1578   r.Inset(-1, -1, -1, -1);
1579   SchedulePaintInRect(r);
1580 }
1581 
PaintTextAndCursor(gfx::Canvas * canvas)1582 void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
1583   TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
1584   canvas->Save();
1585 
1586   // Draw placeholder text if needed.
1587   gfx::RenderText* render_text = GetRenderText();
1588   if (text().empty() && !GetPlaceholderText().empty()) {
1589     canvas->DrawStringRect(GetPlaceholderText(), GetFontList(),
1590         placeholder_text_color(), render_text->display_rect());
1591   }
1592 
1593   // Draw the text, cursor, and selection.
1594   render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ &&
1595                                   !HasSelection());
1596   render_text->Draw(canvas);
1597 
1598   // Draw the detached drop cursor that marks where the text will be dropped.
1599   if (drop_cursor_visible_)
1600     render_text->DrawCursor(canvas, drop_cursor_position_);
1601 
1602   canvas->Restore();
1603 }
1604 
MoveCursorTo(const gfx::Point & point,bool select)1605 void Textfield::MoveCursorTo(const gfx::Point& point, bool select) {
1606   if (model_->MoveCursorTo(point, select))
1607     UpdateAfterChange(false, true);
1608 }
1609 
SelectThroughLastDragLocation()1610 void Textfield::SelectThroughLastDragLocation() {
1611   OnBeforeUserAction();
1612   model_->MoveCursorTo(last_drag_location_, true);
1613   if (aggregated_clicks_ == 1) {
1614     model_->SelectWord();
1615     // Expand the selection so the initially selected word remains selected.
1616     gfx::Range selection = GetRenderText()->selection();
1617     const size_t min = std::min(selection.GetMin(),
1618                                 double_click_word_.GetMin());
1619     const size_t max = std::max(selection.GetMax(),
1620                                 double_click_word_.GetMax());
1621     const bool reversed = selection.is_reversed();
1622     selection.set_start(reversed ? max : min);
1623     selection.set_end(reversed ? min : max);
1624     model_->SelectRange(selection);
1625   }
1626   UpdateAfterChange(false, true);
1627   OnAfterUserAction();
1628 }
1629 
OnCaretBoundsChanged()1630 void Textfield::OnCaretBoundsChanged() {
1631   if (GetInputMethod())
1632     GetInputMethod()->OnCaretBoundsChanged(this);
1633   if (touch_selection_controller_)
1634     touch_selection_controller_->SelectionChanged();
1635 }
1636 
OnBeforeUserAction()1637 void Textfield::OnBeforeUserAction() {
1638   DCHECK(!performing_user_action_);
1639   performing_user_action_ = true;
1640   if (controller_)
1641     controller_->OnBeforeUserAction(this);
1642 }
1643 
OnAfterUserAction()1644 void Textfield::OnAfterUserAction() {
1645   if (controller_)
1646     controller_->OnAfterUserAction(this);
1647   DCHECK(performing_user_action_);
1648   performing_user_action_ = false;
1649 }
1650 
Cut()1651 bool Textfield::Cut() {
1652   if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD &&
1653       model_->Cut()) {
1654     if (controller_)
1655       controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1656     return true;
1657   }
1658   return false;
1659 }
1660 
Copy()1661 bool Textfield::Copy() {
1662   if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) {
1663     if (controller_)
1664       controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1665     return true;
1666   }
1667   return false;
1668 }
1669 
Paste()1670 bool Textfield::Paste() {
1671   if (!read_only() && model_->Paste()) {
1672     if (controller_)
1673       controller_->OnAfterPaste();
1674     return true;
1675   }
1676   return false;
1677 }
1678 
UpdateContextMenu()1679 void Textfield::UpdateContextMenu() {
1680   if (!context_menu_contents_.get()) {
1681     context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1682     context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1683     context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1684     context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1685     context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1686     context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1687     context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
1688     context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1689     context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1690                                                 IDS_APP_SELECT_ALL);
1691     if (controller_)
1692       controller_->UpdateContextMenu(context_menu_contents_.get());
1693   }
1694   context_menu_runner_.reset(new MenuRunner(context_menu_contents_.get()));
1695 }
1696 
TrackMouseClicks(const ui::MouseEvent & event)1697 void Textfield::TrackMouseClicks(const ui::MouseEvent& event) {
1698   if (event.IsOnlyLeftMouseButton()) {
1699     base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
1700     if (time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
1701         !ExceededDragThreshold(event.location() - last_click_location_)) {
1702       // Upon clicking after a triple click, the count should go back to double
1703       // click and alternate between double and triple. This assignment maps
1704       // 0 to 1, 1 to 2, 2 to 1.
1705       aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
1706     } else {
1707       aggregated_clicks_ = 0;
1708     }
1709     last_click_time_ = event.time_stamp();
1710     last_click_location_ = event.location();
1711   }
1712 }
1713 
ImeEditingAllowed() const1714 bool Textfield::ImeEditingAllowed() const {
1715   // Disallow input method editing of password fields.
1716   ui::TextInputType t = GetTextInputType();
1717   return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
1718 }
1719 
RevealPasswordChar(int index)1720 void Textfield::RevealPasswordChar(int index) {
1721   GetRenderText()->SetObscuredRevealIndex(index);
1722   SchedulePaint();
1723 
1724   if (index != -1) {
1725     password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_,
1726         base::Bind(&Textfield::RevealPasswordChar,
1727                    weak_ptr_factory_.GetWeakPtr(), -1));
1728   }
1729 }
1730 
CreateTouchSelectionControllerAndNotifyIt()1731 void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
1732   if (!touch_selection_controller_) {
1733     touch_selection_controller_.reset(
1734         ui::TouchSelectionController::create(this));
1735   }
1736   if (touch_selection_controller_)
1737     touch_selection_controller_->SelectionChanged();
1738 }
1739 
UpdateSelectionClipboard() const1740 void Textfield::UpdateSelectionClipboard() const {
1741 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1742   if (performing_user_action_ && HasSelection()) {
1743     ui::ScopedClipboardWriter(
1744         ui::Clipboard::GetForCurrentThread(),
1745         ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText());
1746     if (controller_)
1747       controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION);
1748   }
1749 #endif
1750 }
1751 
PasteSelectionClipboard(const ui::MouseEvent & event)1752 void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) {
1753   DCHECK(event.IsOnlyMiddleMouseButton());
1754   DCHECK(!read_only());
1755   base::string16 selection_clipboard_text = GetSelectionClipboardText();
1756   if (!selection_clipboard_text.empty()) {
1757     OnBeforeUserAction();
1758     gfx::Range range = GetSelectionModel().selection();
1759     gfx::LogicalCursorDirection affinity = GetSelectionModel().caret_affinity();
1760     const gfx::SelectionModel mouse =
1761         GetRenderText()->FindCursorPosition(event.location());
1762     model_->MoveCursorTo(mouse);
1763     model_->InsertText(selection_clipboard_text);
1764     // Update the new selection range as needed.
1765     if (range.GetMin() >= mouse.caret_pos()) {
1766       const size_t length = selection_clipboard_text.length();
1767       range = gfx::Range(range.start() + length, range.end() + length);
1768     }
1769     model_->MoveCursorTo(gfx::SelectionModel(range, affinity));
1770     UpdateAfterChange(true, true);
1771     OnAfterUserAction();
1772   }
1773 }
1774 
1775 }  // namespace views
1776