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