Add ICU message format support
[chromium-blink-merge.git] / ui / views / controls / textfield / textfield.cc
blob4ae5f1acbc167a1f0a5d38ddf55cdc471cec525a
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.
5 #include "ui/views/controls/textfield/textfield.h"
7 #include <string>
9 #include "base/trace_event/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/ime/input_method.h"
16 #include "ui/base/touch/selection_bound.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/compositor/canvas_painter.h"
19 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
20 #include "ui/events/event.h"
21 #include "ui/events/keycodes/keyboard_codes.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/display.h"
24 #include "ui/gfx/geometry/insets.h"
25 #include "ui/gfx/screen.h"
26 #include "ui/native_theme/native_theme.h"
27 #include "ui/strings/grit/ui_strings.h"
28 #include "ui/views/background.h"
29 #include "ui/views/controls/focusable_border.h"
30 #include "ui/views/controls/label.h"
31 #include "ui/views/controls/menu/menu_runner.h"
32 #include "ui/views/controls/native/native_view_host.h"
33 #include "ui/views/controls/textfield/textfield_controller.h"
34 #include "ui/views/drag_utils.h"
35 #include "ui/views/metrics.h"
36 #include "ui/views/native_cursor.h"
37 #include "ui/views/painter.h"
38 #include "ui/views/views_delegate.h"
39 #include "ui/views/widget/widget.h"
41 #if defined(OS_WIN)
42 #include "base/win/win_util.h"
43 #endif
45 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
46 #include "base/strings/utf_string_conversions.h"
47 #include "ui/events/linux/text_edit_command_auralinux.h"
48 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
49 #endif
51 namespace views {
53 namespace {
55 // Default placeholder text color.
56 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
58 const int kNoCommand = 0;
60 void ConvertRectToScreen(const View* src, gfx::Rect* r) {
61 DCHECK(src);
63 gfx::Point new_origin = r->origin();
64 View::ConvertPointToScreen(src, &new_origin);
65 r->set_origin(new_origin);
68 // Get the drag selection timer delay, respecting animation scaling for testing.
69 int GetDragSelectionDelay() {
70 switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) {
71 case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100;
72 case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: return 25;
73 case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: return 400;
74 case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION: return 1;
75 case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: return 0;
77 return 100;
80 // Get the default command for a given key |event| and selection state.
81 int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) {
82 if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
83 return kNoCommand;
85 const bool shift = event.IsShiftDown();
86 const bool control = event.IsControlDown();
87 const bool alt = event.IsAltDown() || event.IsAltGrDown();
88 switch (event.key_code()) {
89 case ui::VKEY_Z:
90 if (control && !shift && !alt)
91 return IDS_APP_UNDO;
92 return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand;
93 case ui::VKEY_Y:
94 return (control && !alt) ? IDS_APP_REDO : kNoCommand;
95 case ui::VKEY_A:
96 return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand;
97 case ui::VKEY_X:
98 return (control && !alt) ? IDS_APP_CUT : kNoCommand;
99 case ui::VKEY_C:
100 return (control && !alt) ? IDS_APP_COPY : kNoCommand;
101 case ui::VKEY_V:
102 return (control && !alt) ? IDS_APP_PASTE : kNoCommand;
103 case ui::VKEY_RIGHT:
104 // Ignore alt+right, which may be a browser navigation shortcut.
105 if (alt)
106 return kNoCommand;
107 if (!shift)
108 return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT;
109 return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
110 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION;
111 case ui::VKEY_LEFT:
112 // Ignore alt+left, which may be a browser navigation shortcut.
113 if (alt)
114 return kNoCommand;
115 if (!shift)
116 return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT;
117 return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
118 IDS_MOVE_LEFT_AND_MODIFY_SELECTION;
119 case ui::VKEY_HOME:
120 return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
121 IDS_MOVE_TO_BEGINNING_OF_LINE;
122 case ui::VKEY_END:
123 return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
124 IDS_MOVE_TO_END_OF_LINE;
125 case ui::VKEY_BACK:
126 if (!control || has_selection)
127 return IDS_DELETE_BACKWARD;
128 #if defined(OS_LINUX)
129 // Only erase by line break on Linux and ChromeOS.
130 if (shift)
131 return IDS_DELETE_TO_BEGINNING_OF_LINE;
132 #endif
133 return IDS_DELETE_WORD_BACKWARD;
134 case ui::VKEY_DELETE:
135 if (!control || has_selection)
136 return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD;
137 #if defined(OS_LINUX)
138 // Only erase by line break on Linux and ChromeOS.
139 if (shift)
140 return IDS_DELETE_TO_END_OF_LINE;
141 #endif
142 return IDS_DELETE_WORD_FORWARD;
143 case ui::VKEY_INSERT:
144 if (control && !shift)
145 return IDS_APP_COPY;
146 return (shift && !control) ? IDS_APP_PASTE : kNoCommand;
147 default:
148 return kNoCommand;
152 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
153 // Convert a custom text edit |command| to the equivalent views command ID.
154 int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) {
155 const bool select = command.extend_selection();
156 switch (command.command_id()) {
157 case ui::TextEditCommandAuraLinux::COPY:
158 return IDS_APP_COPY;
159 case ui::TextEditCommandAuraLinux::CUT:
160 return IDS_APP_CUT;
161 case ui::TextEditCommandAuraLinux::DELETE_BACKWARD:
162 return IDS_DELETE_BACKWARD;
163 case ui::TextEditCommandAuraLinux::DELETE_FORWARD:
164 return IDS_DELETE_FORWARD;
165 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE:
166 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH:
167 return IDS_DELETE_TO_BEGINNING_OF_LINE;
168 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE:
169 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH:
170 return IDS_DELETE_TO_END_OF_LINE;
171 case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD:
172 return IDS_DELETE_WORD_BACKWARD;
173 case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD:
174 return IDS_DELETE_WORD_FORWARD;
175 case ui::TextEditCommandAuraLinux::INSERT_TEXT:
176 return kNoCommand;
177 case ui::TextEditCommandAuraLinux::MOVE_BACKWARD:
178 if (rtl)
179 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
180 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
181 case ui::TextEditCommandAuraLinux::MOVE_DOWN:
182 return IDS_MOVE_DOWN;
183 case ui::TextEditCommandAuraLinux::MOVE_FORWARD:
184 if (rtl)
185 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
186 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
187 case ui::TextEditCommandAuraLinux::MOVE_LEFT:
188 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
189 case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN:
190 case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP:
191 return kNoCommand;
192 case ui::TextEditCommandAuraLinux::MOVE_RIGHT:
193 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
194 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT:
195 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE:
196 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH:
197 return select ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
198 IDS_MOVE_TO_BEGINNING_OF_LINE;
199 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT:
200 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE:
201 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH:
202 return select ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
203 IDS_MOVE_TO_END_OF_LINE;
204 case ui::TextEditCommandAuraLinux::MOVE_UP:
205 return IDS_MOVE_UP;
206 case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD:
207 if (rtl) {
208 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
209 IDS_MOVE_WORD_RIGHT;
211 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
212 IDS_MOVE_WORD_LEFT;
213 case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD:
214 if (rtl) {
215 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
216 IDS_MOVE_WORD_LEFT;
218 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
219 IDS_MOVE_WORD_RIGHT;
220 case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT:
221 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
222 IDS_MOVE_WORD_LEFT;
223 case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT:
224 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
225 IDS_MOVE_WORD_RIGHT;
226 case ui::TextEditCommandAuraLinux::PASTE:
227 return IDS_APP_PASTE;
228 case ui::TextEditCommandAuraLinux::SELECT_ALL:
229 return IDS_APP_SELECT_ALL;
230 case ui::TextEditCommandAuraLinux::SET_MARK:
231 case ui::TextEditCommandAuraLinux::UNSELECT:
232 case ui::TextEditCommandAuraLinux::INVALID_COMMAND:
233 return kNoCommand;
235 return kNoCommand;
237 #endif
239 } // namespace
241 // static
242 const char Textfield::kViewClassName[] = "Textfield";
243 const int Textfield::kTextPadding = 3;
245 // static
246 size_t Textfield::GetCaretBlinkMs() {
247 static const size_t default_value = 500;
248 #if defined(OS_WIN)
249 static const size_t system_value = ::GetCaretBlinkTime();
250 if (system_value != 0)
251 return (system_value == INFINITE) ? 0 : system_value;
252 #endif
253 return default_value;
256 Textfield::Textfield()
257 : model_(new TextfieldModel(this)),
258 controller_(NULL),
259 scheduled_edit_command_(kNoCommand),
260 read_only_(false),
261 default_width_in_chars_(0),
262 use_default_text_color_(true),
263 use_default_background_color_(true),
264 use_default_selection_text_color_(true),
265 use_default_selection_background_color_(true),
266 text_color_(SK_ColorBLACK),
267 background_color_(SK_ColorWHITE),
268 selection_text_color_(SK_ColorWHITE),
269 selection_background_color_(SK_ColorBLUE),
270 placeholder_text_color_(kDefaultPlaceholderTextColor),
271 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
272 text_input_flags_(0),
273 performing_user_action_(false),
274 skip_input_method_cancel_composition_(false),
275 cursor_visible_(false),
276 drop_cursor_visible_(false),
277 initiating_drag_(false),
278 aggregated_clicks_(0),
279 drag_start_display_offset_(0),
280 touch_handles_hidden_due_to_scroll_(false),
281 weak_ptr_factory_(this) {
282 set_context_menu_controller(this);
283 set_drag_controller(this);
284 SetBorder(scoped_ptr<Border>(new FocusableBorder()));
285 SetFocusable(true);
287 if (ViewsDelegate::GetInstance()) {
288 password_reveal_duration_ =
289 ViewsDelegate::GetInstance()
290 ->GetDefaultTextfieldObscuredRevealDuration();
293 // These allow BrowserView to pass edit commands from the Chrome menu to us
294 // when we're focused by simply asking the FocusManager to
295 // ProcessAccelerator() with the relevant accelerators.
296 AddAccelerator(ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN));
297 AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
298 AddAccelerator(ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN));
301 Textfield::~Textfield() {
302 if (GetInputMethod()) {
303 // The textfield should have been blurred before destroy.
304 DCHECK(this != GetInputMethod()->GetTextInputClient());
308 void Textfield::SetReadOnly(bool read_only) {
309 // Update read-only without changing the focusable state (or active, etc.).
310 read_only_ = read_only;
311 if (GetInputMethod())
312 GetInputMethod()->OnTextInputTypeChanged(this);
313 SetColor(GetTextColor());
314 UpdateBackgroundColor();
317 void Textfield::SetTextInputType(ui::TextInputType type) {
318 GetRenderText()->SetObscured(type == ui::TEXT_INPUT_TYPE_PASSWORD);
319 text_input_type_ = type;
320 OnCaretBoundsChanged();
321 if (GetInputMethod())
322 GetInputMethod()->OnTextInputTypeChanged(this);
323 SchedulePaint();
326 void Textfield::SetTextInputFlags(int flags) {
327 text_input_flags_ = flags;
330 void Textfield::SetText(const base::string16& new_text) {
331 model_->SetText(new_text);
332 OnCaretBoundsChanged();
333 SchedulePaint();
334 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
337 void Textfield::AppendText(const base::string16& new_text) {
338 if (new_text.empty())
339 return;
340 model_->Append(new_text);
341 OnCaretBoundsChanged();
342 SchedulePaint();
345 void Textfield::InsertOrReplaceText(const base::string16& new_text) {
346 if (new_text.empty())
347 return;
348 model_->InsertText(new_text);
349 OnCaretBoundsChanged();
350 SchedulePaint();
353 base::i18n::TextDirection Textfield::GetTextDirection() const {
354 return GetRenderText()->GetDisplayTextDirection();
357 base::string16 Textfield::GetSelectedText() const {
358 return model_->GetSelectedText();
361 void Textfield::SelectAll(bool reversed) {
362 model_->SelectAll(reversed);
363 UpdateSelectionClipboard();
364 UpdateAfterChange(false, true);
367 void Textfield::SelectWordAt(const gfx::Point& point) {
368 model_->MoveCursorTo(point, false);
369 model_->SelectWord();
370 UpdateAfterChange(false, true);
373 void Textfield::ClearSelection() {
374 model_->ClearSelection();
375 UpdateAfterChange(false, true);
378 bool Textfield::HasSelection() const {
379 return !GetSelectedRange().is_empty();
382 SkColor Textfield::GetTextColor() const {
383 if (!use_default_text_color_)
384 return text_color_;
386 return GetNativeTheme()->GetSystemColor(read_only() ?
387 ui::NativeTheme::kColorId_TextfieldReadOnlyColor :
388 ui::NativeTheme::kColorId_TextfieldDefaultColor);
391 void Textfield::SetTextColor(SkColor color) {
392 text_color_ = color;
393 use_default_text_color_ = false;
394 SetColor(color);
397 void Textfield::UseDefaultTextColor() {
398 use_default_text_color_ = true;
399 SetColor(GetTextColor());
402 SkColor Textfield::GetBackgroundColor() const {
403 if (!use_default_background_color_)
404 return background_color_;
406 return GetNativeTheme()->GetSystemColor(read_only() ?
407 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground :
408 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
411 void Textfield::SetBackgroundColor(SkColor color) {
412 background_color_ = color;
413 use_default_background_color_ = false;
414 UpdateBackgroundColor();
417 void Textfield::UseDefaultBackgroundColor() {
418 use_default_background_color_ = true;
419 UpdateBackgroundColor();
422 SkColor Textfield::GetSelectionTextColor() const {
423 return use_default_selection_text_color_ ?
424 GetNativeTheme()->GetSystemColor(
425 ui::NativeTheme::kColorId_TextfieldSelectionColor) :
426 selection_text_color_;
429 void Textfield::SetSelectionTextColor(SkColor color) {
430 selection_text_color_ = color;
431 use_default_selection_text_color_ = false;
432 GetRenderText()->set_selection_color(GetSelectionTextColor());
433 SchedulePaint();
436 void Textfield::UseDefaultSelectionTextColor() {
437 use_default_selection_text_color_ = true;
438 GetRenderText()->set_selection_color(GetSelectionTextColor());
439 SchedulePaint();
442 void Textfield::SetShadows(const gfx::ShadowValues& shadows) {
443 GetRenderText()->set_shadows(shadows);
444 SchedulePaint();
447 SkColor Textfield::GetSelectionBackgroundColor() const {
448 return use_default_selection_background_color_ ?
449 GetNativeTheme()->GetSystemColor(
450 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused) :
451 selection_background_color_;
454 void Textfield::SetSelectionBackgroundColor(SkColor color) {
455 selection_background_color_ = color;
456 use_default_selection_background_color_ = false;
457 GetRenderText()->set_selection_background_focused_color(
458 GetSelectionBackgroundColor());
459 SchedulePaint();
462 void Textfield::UseDefaultSelectionBackgroundColor() {
463 use_default_selection_background_color_ = true;
464 GetRenderText()->set_selection_background_focused_color(
465 GetSelectionBackgroundColor());
466 SchedulePaint();
469 bool Textfield::GetCursorEnabled() const {
470 return GetRenderText()->cursor_enabled();
473 void Textfield::SetCursorEnabled(bool enabled) {
474 GetRenderText()->SetCursorEnabled(enabled);
477 const gfx::FontList& Textfield::GetFontList() const {
478 return GetRenderText()->font_list();
481 void Textfield::SetFontList(const gfx::FontList& font_list) {
482 GetRenderText()->SetFontList(font_list);
483 OnCaretBoundsChanged();
484 PreferredSizeChanged();
487 base::string16 Textfield::GetPlaceholderText() const {
488 return placeholder_text_;
491 gfx::HorizontalAlignment Textfield::GetHorizontalAlignment() const {
492 return GetRenderText()->horizontal_alignment();
495 void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
496 GetRenderText()->SetHorizontalAlignment(alignment);
499 void Textfield::ShowImeIfNeeded() {
500 if (enabled() && !read_only())
501 GetInputMethod()->ShowImeIfNeeded();
504 bool Textfield::IsIMEComposing() const {
505 return model_->HasCompositionText();
508 const gfx::Range& Textfield::GetSelectedRange() const {
509 return GetRenderText()->selection();
512 void Textfield::SelectRange(const gfx::Range& range) {
513 model_->SelectRange(range);
514 UpdateAfterChange(false, true);
517 const gfx::SelectionModel& Textfield::GetSelectionModel() const {
518 return GetRenderText()->selection_model();
521 void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
522 model_->SelectSelectionModel(sel);
523 UpdateAfterChange(false, true);
526 size_t Textfield::GetCursorPosition() const {
527 return model_->GetCursorPosition();
530 void Textfield::SetColor(SkColor value) {
531 GetRenderText()->SetColor(value);
532 SchedulePaint();
535 void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
536 GetRenderText()->ApplyColor(value, range);
537 SchedulePaint();
540 void Textfield::SetStyle(gfx::TextStyle style, bool value) {
541 GetRenderText()->SetStyle(style, value);
542 SchedulePaint();
545 void Textfield::ApplyStyle(gfx::TextStyle style,
546 bool value,
547 const gfx::Range& range) {
548 GetRenderText()->ApplyStyle(style, value, range);
549 SchedulePaint();
552 void Textfield::ClearEditHistory() {
553 model_->ClearEditHistory();
556 void Textfield::SetAccessibleName(const base::string16& name) {
557 accessible_name_ = name;
560 void Textfield::ExecuteCommand(int command_id) {
561 ExecuteCommand(command_id, ui::EF_NONE);
564 void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
565 focus_painter_ = focus_painter.Pass();
568 bool Textfield::HasTextBeingDragged() {
569 return initiating_drag_;
572 ////////////////////////////////////////////////////////////////////////////////
573 // Textfield, View overrides:
575 gfx::Insets Textfield::GetInsets() const {
576 gfx::Insets insets = View::GetInsets();
577 insets += gfx::Insets(kTextPadding, kTextPadding, kTextPadding, kTextPadding);
578 return insets;
581 int Textfield::GetBaseline() const {
582 return GetInsets().top() + GetRenderText()->GetBaseline();
585 gfx::Size Textfield::GetPreferredSize() const {
586 const gfx::Insets& insets = GetInsets();
587 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) +
588 insets.width(), GetFontList().GetHeight() + insets.height());
591 const char* Textfield::GetClassName() const {
592 return kViewClassName;
595 gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) {
596 bool in_selection = GetRenderText()->IsPointInSelection(event.location());
597 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
598 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
599 return text_cursor ? GetNativeIBeamCursor() : gfx::kNullCursor;
602 bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
603 TrackMouseClicks(event);
605 if (!controller_ || !controller_->HandleMouseEvent(this, event)) {
606 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) {
607 RequestFocus();
608 ShowImeIfNeeded();
611 if (event.IsOnlyLeftMouseButton()) {
612 OnBeforeUserAction();
613 initiating_drag_ = false;
614 switch (aggregated_clicks_) {
615 case 0:
616 if (GetRenderText()->IsPointInSelection(event.location()))
617 initiating_drag_ = true;
618 else
619 MoveCursorTo(event.location(), event.IsShiftDown());
620 break;
621 case 1:
622 SelectWordAt(event.location());
623 double_click_word_ = GetRenderText()->selection();
624 break;
625 case 2:
626 SelectAll(false);
627 break;
628 default:
629 NOTREACHED();
631 OnAfterUserAction();
634 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
635 if (event.IsOnlyMiddleMouseButton()) {
636 if (GetRenderText()->IsPointInSelection(event.location())) {
637 OnBeforeUserAction();
638 ClearSelection();
639 ui::ScopedClipboardWriter(
640 ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16());
641 OnAfterUserAction();
642 } else if (!read_only()) {
643 PasteSelectionClipboard(event);
646 #endif
649 return true;
652 bool Textfield::OnMouseDragged(const ui::MouseEvent& event) {
653 last_drag_location_ = event.location();
655 // Don't adjust the cursor on a potential drag and drop, or if the mouse
656 // movement from the last mouse click does not exceed the drag threshold.
657 if (initiating_drag_ || !event.IsOnlyLeftMouseButton() ||
658 !ExceededDragThreshold(last_drag_location_ - last_click_location_)) {
659 return true;
662 // A timer is used to continuously scroll while selecting beyond side edges.
663 const int x = event.location().x();
664 if ((x >= 0 && x <= width()) || GetDragSelectionDelay() == 0) {
665 drag_selection_timer_.Stop();
666 SelectThroughLastDragLocation();
667 } else if (!drag_selection_timer_.IsRunning()) {
668 // Select through the edge of the visible text, then start the scroll timer.
669 last_drag_location_.set_x(std::min(std::max(0, x), width()));
670 SelectThroughLastDragLocation();
671 drag_selection_timer_.Start(
672 FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()),
673 this, &Textfield::SelectThroughLastDragLocation);
676 return true;
679 void Textfield::OnMouseReleased(const ui::MouseEvent& event) {
680 OnBeforeUserAction();
681 drag_selection_timer_.Stop();
682 // Cancel suspected drag initiations, the user was clicking in the selection.
683 if (initiating_drag_)
684 MoveCursorTo(event.location(), false);
685 initiating_drag_ = false;
686 UpdateSelectionClipboard();
687 OnAfterUserAction();
690 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
691 int edit_command = scheduled_edit_command_;
692 scheduled_edit_command_ = kNoCommand;
694 // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify
695 // it isn't null before proceeding.
696 base::WeakPtr<Textfield> textfield(weak_ptr_factory_.GetWeakPtr());
698 bool handled = controller_ && controller_->HandleKeyEvent(this, event);
700 if (!textfield)
701 return handled;
703 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
704 ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
705 ui::GetTextEditKeyBindingsDelegate();
706 std::vector<ui::TextEditCommandAuraLinux> commands;
707 if (!handled && delegate && delegate->MatchEvent(event, &commands)) {
708 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
709 for (size_t i = 0; i < commands.size(); ++i) {
710 const int command = GetViewsCommand(commands[i], rtl);
711 if (IsCommandIdEnabled(command)) {
712 ExecuteCommand(command);
713 handled = true;
716 return handled;
718 #endif
720 if (edit_command == kNoCommand)
721 edit_command = GetCommandForKeyEvent(event, HasSelection());
723 if (!handled && IsCommandIdEnabled(edit_command)) {
724 ExecuteCommand(edit_command);
725 handled = true;
727 return handled;
730 void Textfield::OnGestureEvent(ui::GestureEvent* event) {
731 switch (event->type()) {
732 case ui::ET_GESTURE_TAP_DOWN:
733 RequestFocus();
734 ShowImeIfNeeded();
735 event->SetHandled();
736 break;
737 case ui::ET_GESTURE_TAP:
738 if (event->details().tap_count() == 1) {
739 // If tap is on the selection and touch handles are not present, handles
740 // should be shown without changing selection. Otherwise, cursor should
741 // be moved to the tap location.
742 if (touch_selection_controller_ ||
743 !GetRenderText()->IsPointInSelection(event->location())) {
744 OnBeforeUserAction();
745 MoveCursorTo(event->location(), false);
746 OnAfterUserAction();
748 } else if (event->details().tap_count() == 2) {
749 OnBeforeUserAction();
750 SelectWordAt(event->location());
751 OnAfterUserAction();
752 } else {
753 OnBeforeUserAction();
754 SelectAll(false);
755 OnAfterUserAction();
757 CreateTouchSelectionControllerAndNotifyIt();
758 #if defined(OS_WIN)
759 if (!read_only())
760 base::win::DisplayVirtualKeyboard();
761 #endif
762 event->SetHandled();
763 break;
764 case ui::ET_GESTURE_LONG_PRESS:
765 if (!GetRenderText()->IsPointInSelection(event->location())) {
766 // If long-press happens outside selection, select word and try to
767 // activate touch selection.
768 OnBeforeUserAction();
769 SelectWordAt(event->location());
770 OnAfterUserAction();
771 CreateTouchSelectionControllerAndNotifyIt();
772 // If touch selection activated successfully, mark event as handled so
773 // that the regular context menu is not shown.
774 if (touch_selection_controller_)
775 event->SetHandled();
776 } else {
777 // If long-press happens on the selection, deactivate touch selection
778 // and try to initiate drag-drop. If drag-drop is not enabled, context
779 // menu will be shown. Event is not marked as handled to let Views
780 // handle drag-drop or context menu.
781 DestroyTouchSelection();
782 initiating_drag_ = switches::IsTouchDragDropEnabled();
784 break;
785 case ui::ET_GESTURE_LONG_TAP:
786 // If touch selection is enabled, the context menu on long tap will be
787 // shown by the |touch_selection_controller_|, hence we mark the event
788 // handled so Views does not try to show context menu on it.
789 if (touch_selection_controller_)
790 event->SetHandled();
791 break;
792 case ui::ET_GESTURE_SCROLL_BEGIN:
793 touch_handles_hidden_due_to_scroll_ = touch_selection_controller_ != NULL;
794 DestroyTouchSelection();
795 drag_start_location_ = event->location();
796 drag_start_display_offset_ =
797 GetRenderText()->GetUpdatedDisplayOffset().x();
798 event->SetHandled();
799 break;
800 case ui::ET_GESTURE_SCROLL_UPDATE: {
801 int new_offset = drag_start_display_offset_ + event->location().x() -
802 drag_start_location_.x();
803 GetRenderText()->SetDisplayOffset(new_offset);
804 SchedulePaint();
805 event->SetHandled();
806 break;
808 case ui::ET_GESTURE_SCROLL_END:
809 case ui::ET_SCROLL_FLING_START:
810 if (touch_handles_hidden_due_to_scroll_) {
811 CreateTouchSelectionControllerAndNotifyIt();
812 touch_handles_hidden_due_to_scroll_ = false;
814 event->SetHandled();
815 break;
816 default:
817 return;
821 // This function is called by BrowserView to execute clipboard commands.
822 bool Textfield::AcceleratorPressed(const ui::Accelerator& accelerator) {
823 ui::KeyEvent event(accelerator.type(), accelerator.key_code(),
824 accelerator.modifiers());
825 ExecuteCommand(GetCommandForKeyEvent(event, HasSelection()));
826 return true;
829 bool Textfield::CanHandleAccelerators() const {
830 return GetRenderText()->focused() && View::CanHandleAccelerators();
833 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
834 SelectAll(false);
837 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
838 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
839 // Skip any accelerator handling that conflicts with custom keybindings.
840 ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
841 ui::GetTextEditKeyBindingsDelegate();
842 std::vector<ui::TextEditCommandAuraLinux> commands;
843 if (delegate && delegate->MatchEvent(event, &commands)) {
844 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
845 for (size_t i = 0; i < commands.size(); ++i)
846 if (IsCommandIdEnabled(GetViewsCommand(commands[i], rtl)))
847 return true;
849 #endif
851 // Skip backspace accelerator handling; editable textfields handle this key.
852 // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes.
853 const bool is_backspace = event.key_code() == ui::VKEY_BACK;
854 return (is_backspace && !read_only()) || event.IsUnicodeKeyCode();
857 bool Textfield::GetDropFormats(
858 int* formats,
859 std::set<OSExchangeData::CustomFormat>* custom_formats) {
860 if (!enabled() || read_only())
861 return false;
862 // TODO(msw): Can we support URL, FILENAME, etc.?
863 *formats = ui::OSExchangeData::STRING;
864 if (controller_)
865 controller_->AppendDropFormats(formats, custom_formats);
866 return true;
869 bool Textfield::CanDrop(const OSExchangeData& data) {
870 int formats;
871 std::set<OSExchangeData::CustomFormat> custom_formats;
872 GetDropFormats(&formats, &custom_formats);
873 return enabled() && !read_only() &&
874 data.HasAnyFormat(formats, custom_formats);
877 int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) {
878 DCHECK(CanDrop(event.data()));
879 gfx::RenderText* render_text = GetRenderText();
880 const gfx::Range& selection = render_text->selection();
881 drop_cursor_position_ = render_text->FindCursorPosition(event.location());
882 bool in_selection = !selection.is_empty() &&
883 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos()));
884 drop_cursor_visible_ = !in_selection;
885 // TODO(msw): Pan over text when the user drags to the visible text edge.
886 OnCaretBoundsChanged();
887 SchedulePaint();
889 if (initiating_drag_) {
890 if (in_selection)
891 return ui::DragDropTypes::DRAG_NONE;
892 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
893 ui::DragDropTypes::DRAG_MOVE;
895 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
898 void Textfield::OnDragExited() {
899 drop_cursor_visible_ = false;
900 SchedulePaint();
903 int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) {
904 DCHECK(CanDrop(event.data()));
905 drop_cursor_visible_ = false;
907 if (controller_) {
908 int drag_operation = controller_->OnDrop(event.data());
909 if (drag_operation != ui::DragDropTypes::DRAG_NONE)
910 return drag_operation;
913 gfx::RenderText* render_text = GetRenderText();
914 DCHECK(!initiating_drag_ ||
915 !render_text->IsPointInSelection(event.location()));
916 OnBeforeUserAction();
917 skip_input_method_cancel_composition_ = true;
919 gfx::SelectionModel drop_destination_model =
920 render_text->FindCursorPosition(event.location());
921 base::string16 new_text;
922 event.data().GetString(&new_text);
924 // Delete the current selection for a drag and drop within this view.
925 const bool move = initiating_drag_ && !event.IsControlDown() &&
926 event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
927 if (move) {
928 // Adjust the drop destination if it is on or after the current selection.
929 size_t pos = drop_destination_model.caret_pos();
930 pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length();
931 model_->DeleteSelectionAndInsertTextAt(new_text, pos);
932 } else {
933 model_->MoveCursorTo(drop_destination_model);
934 // Drop always inserts text even if the textfield is not in insert mode.
935 model_->InsertText(new_text);
937 skip_input_method_cancel_composition_ = false;
938 UpdateAfterChange(true, true);
939 OnAfterUserAction();
940 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
943 void Textfield::OnDragDone() {
944 initiating_drag_ = false;
945 drop_cursor_visible_ = false;
948 void Textfield::GetAccessibleState(ui::AXViewState* state) {
949 state->role = ui::AX_ROLE_TEXT_FIELD;
950 state->name = accessible_name_;
951 if (read_only())
952 state->AddStateFlag(ui::AX_STATE_READ_ONLY);
953 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) {
954 state->AddStateFlag(ui::AX_STATE_PROTECTED);
955 state->value = base::string16(text().size(), '*');
956 } else {
957 state->value = text();
959 const gfx::Range range = GetSelectedRange();
960 state->selection_start = range.start();
961 state->selection_end = range.end();
963 if (!read_only()) {
964 state->set_value_callback =
965 base::Bind(&Textfield::AccessibilitySetValue,
966 weak_ptr_factory_.GetWeakPtr());
970 void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) {
971 // Textfield insets include a reasonable amount of whitespace on all sides of
972 // the default font list. Fallback fonts with larger heights may paint over
973 // the vertical whitespace as needed. Alternate solutions involve undesirable
974 // behavior like changing the default font size, shrinking some fallback fonts
975 // beyond their legibility, or enlarging controls dynamically with content.
976 gfx::Rect bounds = GetContentsBounds();
977 // GetContentsBounds() does not actually use the local GetInsets() override.
978 bounds.Inset(gfx::Insets(0, kTextPadding, 0, kTextPadding));
979 GetRenderText()->SetDisplayRect(bounds);
980 OnCaretBoundsChanged();
983 bool Textfield::GetNeedsNotificationWhenVisibleBoundsChange() const {
984 return true;
987 void Textfield::OnVisibleBoundsChanged() {
988 if (touch_selection_controller_)
989 touch_selection_controller_->SelectionChanged();
992 void Textfield::OnEnabledChanged() {
993 View::OnEnabledChanged();
994 if (GetInputMethod())
995 GetInputMethod()->OnTextInputTypeChanged(this);
996 SchedulePaint();
999 void Textfield::OnPaint(gfx::Canvas* canvas) {
1000 OnPaintBackground(canvas);
1001 PaintTextAndCursor(canvas);
1002 OnPaintBorder(canvas);
1005 void Textfield::OnFocus() {
1006 GetRenderText()->set_focused(true);
1007 cursor_visible_ = true;
1008 SchedulePaint();
1009 if (GetInputMethod())
1010 GetInputMethod()->SetFocusedTextInputClient(this);
1011 OnCaretBoundsChanged();
1013 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1014 if (caret_blink_ms != 0) {
1015 cursor_repaint_timer_.Start(FROM_HERE,
1016 base::TimeDelta::FromMilliseconds(caret_blink_ms), this,
1017 &Textfield::UpdateCursor);
1020 View::OnFocus();
1021 SchedulePaint();
1024 void Textfield::OnBlur() {
1025 GetRenderText()->set_focused(false);
1026 if (GetInputMethod())
1027 GetInputMethod()->DetachTextInputClient(this);
1028 cursor_repaint_timer_.Stop();
1029 if (cursor_visible_) {
1030 cursor_visible_ = false;
1031 RepaintCursor();
1034 DestroyTouchSelection();
1036 // Border typically draws focus indicator.
1037 SchedulePaint();
1040 gfx::Point Textfield::GetKeyboardContextMenuLocation() {
1041 return GetCaretBounds().bottom_right();
1044 void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) {
1045 gfx::RenderText* render_text = GetRenderText();
1046 render_text->SetColor(GetTextColor());
1047 UpdateBackgroundColor();
1048 render_text->set_cursor_color(GetTextColor());
1049 render_text->set_selection_color(GetSelectionTextColor());
1050 render_text->set_selection_background_focused_color(
1051 GetSelectionBackgroundColor());
1054 ////////////////////////////////////////////////////////////////////////////////
1055 // Textfield, TextfieldModel::Delegate overrides:
1057 void Textfield::OnCompositionTextConfirmedOrCleared() {
1058 if (!skip_input_method_cancel_composition_)
1059 GetInputMethod()->CancelComposition(this);
1062 ////////////////////////////////////////////////////////////////////////////////
1063 // Textfield, ContextMenuController overrides:
1065 void Textfield::ShowContextMenuForView(View* source,
1066 const gfx::Point& point,
1067 ui::MenuSourceType source_type) {
1068 UpdateContextMenu();
1069 ignore_result(context_menu_runner_->RunMenuAt(GetWidget(),
1070 NULL,
1071 gfx::Rect(point, gfx::Size()),
1072 MENU_ANCHOR_TOPLEFT,
1073 source_type));
1076 ////////////////////////////////////////////////////////////////////////////////
1077 // Textfield, DragController overrides:
1079 void Textfield::WriteDragDataForView(View* sender,
1080 const gfx::Point& press_pt,
1081 OSExchangeData* data) {
1082 const base::string16& selected_text(GetSelectedText());
1083 data->SetString(selected_text);
1084 Label label(selected_text, GetFontList());
1085 label.SetBackgroundColor(GetBackgroundColor());
1086 label.SetSubpixelRenderingEnabled(false);
1087 gfx::Size size(label.GetPreferredSize());
1088 gfx::NativeView native_view = GetWidget()->GetNativeView();
1089 gfx::Display display = gfx::Screen::GetScreenFor(native_view)->
1090 GetDisplayNearestWindow(native_view);
1091 size.SetToMin(gfx::Size(display.size().width(), height()));
1092 label.SetBoundsRect(gfx::Rect(size));
1093 scoped_ptr<gfx::Canvas> canvas(
1094 GetCanvasForDragImage(GetWidget(), label.size()));
1095 label.SetEnabledColor(GetTextColor());
1096 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1097 // Desktop Linux Aura does not yet support transparency in drag images.
1098 canvas->DrawColor(GetBackgroundColor());
1099 #endif
1100 label.Paint(ui::CanvasPainter(canvas.get(), 1.f).context());
1101 const gfx::Vector2d kOffset(-15, 0);
1102 drag_utils::SetDragImageOnDataObject(*canvas, kOffset, data);
1103 if (controller_)
1104 controller_->OnWriteDragData(data);
1107 int Textfield::GetDragOperationsForView(View* sender, const gfx::Point& p) {
1108 int drag_operations = ui::DragDropTypes::DRAG_COPY;
1109 if (!enabled() || text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD ||
1110 !GetRenderText()->IsPointInSelection(p)) {
1111 drag_operations = ui::DragDropTypes::DRAG_NONE;
1112 } else if (sender == this && !read_only()) {
1113 drag_operations =
1114 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
1116 if (controller_)
1117 controller_->OnGetDragOperationsForTextfield(&drag_operations);
1118 return drag_operations;
1121 bool Textfield::CanStartDragForView(View* sender,
1122 const gfx::Point& press_pt,
1123 const gfx::Point& p) {
1124 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt);
1127 ////////////////////////////////////////////////////////////////////////////////
1128 // Textfield, ui::TouchEditable overrides:
1130 void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) {
1131 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1132 return;
1134 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start);
1135 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end);
1136 gfx::SelectionModel selection(
1137 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()),
1138 end_caret.caret_affinity());
1140 OnBeforeUserAction();
1141 SelectSelectionModel(selection);
1142 OnAfterUserAction();
1145 void Textfield::MoveCaretTo(const gfx::Point& point) {
1146 SelectRect(point, point);
1149 void Textfield::GetSelectionEndPoints(ui::SelectionBound* anchor,
1150 ui::SelectionBound* focus) {
1151 gfx::RenderText* render_text = GetRenderText();
1152 const gfx::SelectionModel& sel = render_text->selection_model();
1153 gfx::SelectionModel start_sel =
1154 render_text->GetSelectionModelForSelectionStart();
1155 gfx::Rect r1 = render_text->GetCursorBounds(start_sel, true);
1156 gfx::Rect r2 = render_text->GetCursorBounds(sel, true);
1158 anchor->SetEdge(r1.origin(), r1.bottom_left());
1159 focus->SetEdge(r2.origin(), r2.bottom_left());
1161 // Determine the SelectionBound's type for focus and anchor.
1162 // TODO(mfomitchev): Ideally we should have different logical directions for
1163 // start and end to support proper handle direction for mixed LTR/RTL text.
1164 const bool ltr = GetTextDirection() != base::i18n::RIGHT_TO_LEFT;
1165 size_t anchor_position_index = sel.selection().start();
1166 size_t focus_position_index = sel.selection().end();
1168 if (anchor_position_index == focus_position_index) {
1169 anchor->set_type(ui::SelectionBound::CENTER);
1170 focus->set_type(ui::SelectionBound::CENTER);
1171 } else if ((ltr && anchor_position_index < focus_position_index) ||
1172 (!ltr && anchor_position_index > focus_position_index)) {
1173 anchor->set_type(ui::SelectionBound::LEFT);
1174 focus->set_type(ui::SelectionBound::RIGHT);
1175 } else {
1176 anchor->set_type(ui::SelectionBound::RIGHT);
1177 focus->set_type(ui::SelectionBound::LEFT);
1181 gfx::Rect Textfield::GetBounds() {
1182 return GetLocalBounds();
1185 gfx::NativeView Textfield::GetNativeView() const {
1186 return GetWidget()->GetNativeView();
1189 void Textfield::ConvertPointToScreen(gfx::Point* point) {
1190 View::ConvertPointToScreen(this, point);
1193 void Textfield::ConvertPointFromScreen(gfx::Point* point) {
1194 View::ConvertPointFromScreen(this, point);
1197 bool Textfield::DrawsHandles() {
1198 return false;
1201 void Textfield::OpenContextMenu(const gfx::Point& anchor) {
1202 DestroyTouchSelection();
1203 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU);
1206 void Textfield::DestroyTouchSelection() {
1207 touch_selection_controller_.reset();
1210 ////////////////////////////////////////////////////////////////////////////////
1211 // Textfield, ui::SimpleMenuModel::Delegate overrides:
1213 bool Textfield::IsCommandIdChecked(int command_id) const {
1214 return true;
1217 bool Textfield::IsCommandIdEnabled(int command_id) const {
1218 base::string16 result;
1219 bool editable = !read_only();
1220 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
1221 switch (command_id) {
1222 case IDS_APP_UNDO:
1223 return editable && model_->CanUndo();
1224 case IDS_APP_REDO:
1225 return editable && model_->CanRedo();
1226 case IDS_APP_CUT:
1227 return editable && readable && model_->HasSelection();
1228 case IDS_APP_COPY:
1229 return readable && model_->HasSelection();
1230 case IDS_APP_PASTE:
1231 ui::Clipboard::GetForCurrentThread()->ReadText(
1232 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
1233 return editable && !result.empty();
1234 case IDS_APP_DELETE:
1235 return editable && model_->HasSelection();
1236 case IDS_APP_SELECT_ALL:
1237 return !text().empty();
1238 case IDS_DELETE_FORWARD:
1239 case IDS_DELETE_BACKWARD:
1240 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1241 case IDS_DELETE_TO_END_OF_LINE:
1242 case IDS_DELETE_WORD_BACKWARD:
1243 case IDS_DELETE_WORD_FORWARD:
1244 return editable;
1245 case IDS_MOVE_LEFT:
1246 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1247 case IDS_MOVE_RIGHT:
1248 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1249 case IDS_MOVE_WORD_LEFT:
1250 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1251 case IDS_MOVE_WORD_RIGHT:
1252 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1253 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1254 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1255 case IDS_MOVE_TO_END_OF_LINE:
1256 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1257 return true;
1258 default:
1259 return false;
1263 bool Textfield::GetAcceleratorForCommandId(int command_id,
1264 ui::Accelerator* accelerator) {
1265 switch (command_id) {
1266 case IDS_APP_UNDO:
1267 *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN);
1268 return true;
1270 case IDS_APP_CUT:
1271 *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
1272 return true;
1274 case IDS_APP_COPY:
1275 *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
1276 return true;
1278 case IDS_APP_PASTE:
1279 *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
1280 return true;
1282 case IDS_APP_SELECT_ALL:
1283 *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
1284 return true;
1286 default:
1287 return false;
1291 void Textfield::ExecuteCommand(int command_id, int event_flags) {
1292 DestroyTouchSelection();
1293 if (!IsCommandIdEnabled(command_id))
1294 return;
1296 bool text_changed = false;
1297 bool cursor_changed = false;
1298 bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
1299 gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
1300 gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
1301 gfx::SelectionModel selection_model = GetSelectionModel();
1303 OnBeforeUserAction();
1304 switch (command_id) {
1305 case IDS_APP_UNDO:
1306 text_changed = cursor_changed = model_->Undo();
1307 break;
1308 case IDS_APP_REDO:
1309 text_changed = cursor_changed = model_->Redo();
1310 break;
1311 case IDS_APP_CUT:
1312 text_changed = cursor_changed = Cut();
1313 break;
1314 case IDS_APP_COPY:
1315 Copy();
1316 break;
1317 case IDS_APP_PASTE:
1318 text_changed = cursor_changed = Paste();
1319 break;
1320 case IDS_APP_DELETE:
1321 text_changed = cursor_changed = model_->Delete();
1322 break;
1323 case IDS_APP_SELECT_ALL:
1324 SelectAll(false);
1325 break;
1326 case IDS_DELETE_BACKWARD:
1327 text_changed = cursor_changed = model_->Backspace();
1328 break;
1329 case IDS_DELETE_FORWARD:
1330 text_changed = cursor_changed = model_->Delete();
1331 break;
1332 case IDS_DELETE_TO_END_OF_LINE:
1333 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1334 text_changed = cursor_changed = model_->Delete();
1335 break;
1336 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1337 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1338 text_changed = cursor_changed = model_->Backspace();
1339 break;
1340 case IDS_DELETE_WORD_BACKWARD:
1341 model_->MoveCursor(gfx::WORD_BREAK, begin, true);
1342 text_changed = cursor_changed = model_->Backspace();
1343 break;
1344 case IDS_DELETE_WORD_FORWARD:
1345 model_->MoveCursor(gfx::WORD_BREAK, end, true);
1346 text_changed = cursor_changed = model_->Delete();
1347 break;
1348 case IDS_MOVE_LEFT:
1349 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
1350 break;
1351 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1352 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
1353 break;
1354 case IDS_MOVE_RIGHT:
1355 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
1356 break;
1357 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1358 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
1359 break;
1360 case IDS_MOVE_WORD_LEFT:
1361 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
1362 break;
1363 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1364 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
1365 break;
1366 case IDS_MOVE_WORD_RIGHT:
1367 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
1368 break;
1369 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1370 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
1371 break;
1372 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1373 model_->MoveCursor(gfx::LINE_BREAK, begin, false);
1374 break;
1375 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1376 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1377 break;
1378 case IDS_MOVE_TO_END_OF_LINE:
1379 model_->MoveCursor(gfx::LINE_BREAK, end, false);
1380 break;
1381 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1382 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1383 break;
1384 default:
1385 NOTREACHED();
1386 break;
1389 cursor_changed |= GetSelectionModel() != selection_model;
1390 if (cursor_changed)
1391 UpdateSelectionClipboard();
1392 UpdateAfterChange(text_changed, cursor_changed);
1393 OnAfterUserAction();
1396 ////////////////////////////////////////////////////////////////////////////////
1397 // Textfield, ui::TextInputClient overrides:
1399 void Textfield::SetCompositionText(const ui::CompositionText& composition) {
1400 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1401 return;
1403 OnBeforeUserAction();
1404 skip_input_method_cancel_composition_ = true;
1405 model_->SetCompositionText(composition);
1406 skip_input_method_cancel_composition_ = false;
1407 UpdateAfterChange(true, true);
1408 OnAfterUserAction();
1411 void Textfield::ConfirmCompositionText() {
1412 if (!model_->HasCompositionText())
1413 return;
1415 OnBeforeUserAction();
1416 skip_input_method_cancel_composition_ = true;
1417 model_->ConfirmCompositionText();
1418 skip_input_method_cancel_composition_ = false;
1419 UpdateAfterChange(true, true);
1420 OnAfterUserAction();
1423 void Textfield::ClearCompositionText() {
1424 if (!model_->HasCompositionText())
1425 return;
1427 OnBeforeUserAction();
1428 skip_input_method_cancel_composition_ = true;
1429 model_->CancelCompositionText();
1430 skip_input_method_cancel_composition_ = false;
1431 UpdateAfterChange(true, true);
1432 OnAfterUserAction();
1435 void Textfield::InsertText(const base::string16& new_text) {
1436 // TODO(suzhe): Filter invalid characters.
1437 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty())
1438 return;
1440 OnBeforeUserAction();
1441 skip_input_method_cancel_composition_ = true;
1442 if (GetRenderText()->insert_mode())
1443 model_->InsertText(new_text);
1444 else
1445 model_->ReplaceText(new_text);
1446 skip_input_method_cancel_composition_ = false;
1447 UpdateAfterChange(true, true);
1448 OnAfterUserAction();
1451 void Textfield::InsertChar(base::char16 ch, int flags) {
1452 const int kControlModifierMask = ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
1453 ui::EF_COMMAND_DOWN | ui::EF_ALTGR_DOWN |
1454 ui::EF_MOD3_DOWN;
1456 // Filter out all control characters, including tab and new line characters,
1457 // and all characters with Alt modifier. But allow characters with the AltGr
1458 // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a
1459 // different flag that we don't care about.
1460 const bool should_insert_char =
1461 ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1462 (flags & kControlModifierMask) != ui::EF_ALT_DOWN;
1463 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char)
1464 return;
1466 DoInsertChar(ch);
1468 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD &&
1469 password_reveal_duration_ != base::TimeDelta()) {
1470 const size_t change_offset = model_->GetCursorPosition();
1471 DCHECK_GT(change_offset, 0u);
1472 RevealPasswordChar(change_offset - 1);
1476 ui::TextInputType Textfield::GetTextInputType() const {
1477 if (read_only() || !enabled())
1478 return ui::TEXT_INPUT_TYPE_NONE;
1479 return text_input_type_;
1482 ui::TextInputMode Textfield::GetTextInputMode() const {
1483 return ui::TEXT_INPUT_MODE_DEFAULT;
1486 int Textfield::GetTextInputFlags() const {
1487 return text_input_flags_;
1490 bool Textfield::CanComposeInline() const {
1491 return true;
1494 gfx::Rect Textfield::GetCaretBounds() const {
1495 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds();
1496 ConvertRectToScreen(this, &rect);
1497 return rect;
1500 bool Textfield::GetCompositionCharacterBounds(uint32 index,
1501 gfx::Rect* rect) const {
1502 DCHECK(rect);
1503 if (!HasCompositionText())
1504 return false;
1505 gfx::Range composition_range;
1506 model_->GetCompositionTextRange(&composition_range);
1507 DCHECK(!composition_range.is_empty());
1509 size_t text_index = composition_range.start() + index;
1510 if (composition_range.end() <= text_index)
1511 return false;
1512 gfx::RenderText* render_text = GetRenderText();
1513 if (!render_text->IsValidCursorIndex(text_index)) {
1514 text_index = render_text->IndexOfAdjacentGrapheme(
1515 text_index, gfx::CURSOR_BACKWARD);
1517 if (text_index < composition_range.start())
1518 return false;
1519 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD);
1520 *rect = render_text->GetCursorBounds(caret, false);
1521 ConvertRectToScreen(this, rect);
1522 return true;
1525 bool Textfield::HasCompositionText() const {
1526 return model_->HasCompositionText();
1529 bool Textfield::GetTextRange(gfx::Range* range) const {
1530 if (!ImeEditingAllowed())
1531 return false;
1533 model_->GetTextRange(range);
1534 return true;
1537 bool Textfield::GetCompositionTextRange(gfx::Range* range) const {
1538 if (!ImeEditingAllowed())
1539 return false;
1541 model_->GetCompositionTextRange(range);
1542 return true;
1545 bool Textfield::GetSelectionRange(gfx::Range* range) const {
1546 if (!ImeEditingAllowed())
1547 return false;
1548 *range = GetRenderText()->selection();
1549 return true;
1552 bool Textfield::SetSelectionRange(const gfx::Range& range) {
1553 if (!ImeEditingAllowed() || !range.IsValid())
1554 return false;
1555 OnBeforeUserAction();
1556 SelectRange(range);
1557 OnAfterUserAction();
1558 return true;
1561 bool Textfield::DeleteRange(const gfx::Range& range) {
1562 if (!ImeEditingAllowed() || range.is_empty())
1563 return false;
1565 OnBeforeUserAction();
1566 model_->SelectRange(range);
1567 if (model_->HasSelection()) {
1568 model_->DeleteSelection();
1569 UpdateAfterChange(true, true);
1571 OnAfterUserAction();
1572 return true;
1575 bool Textfield::GetTextFromRange(const gfx::Range& range,
1576 base::string16* range_text) const {
1577 if (!ImeEditingAllowed() || !range.IsValid())
1578 return false;
1580 gfx::Range text_range;
1581 if (!GetTextRange(&text_range) || !text_range.Contains(range))
1582 return false;
1584 *range_text = model_->GetTextFromRange(range);
1585 return true;
1588 void Textfield::OnInputMethodChanged() {}
1590 bool Textfield::ChangeTextDirectionAndLayoutAlignment(
1591 base::i18n::TextDirection direction) {
1592 // Restore text directionality mode when the indicated direction matches the
1593 // current forced mode; otherwise, force the mode indicated. This helps users
1594 // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1595 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ?
1596 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
1597 if (mode == GetRenderText()->directionality_mode())
1598 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
1599 else
1600 GetRenderText()->SetDirectionalityMode(mode);
1601 SchedulePaint();
1602 return true;
1605 void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) {
1606 gfx::Range range = GetRenderText()->selection();
1607 DCHECK_GE(range.start(), before);
1609 range.set_start(range.start() - before);
1610 range.set_end(range.end() + after);
1611 gfx::Range text_range;
1612 if (GetTextRange(&text_range) && text_range.Contains(range))
1613 DeleteRange(range);
1616 void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {}
1618 bool Textfield::IsEditCommandEnabled(int command_id) {
1619 return IsCommandIdEnabled(command_id);
1622 void Textfield::SetEditCommandForNextKeyEvent(int command_id) {
1623 DCHECK_EQ(kNoCommand, scheduled_edit_command_);
1624 scheduled_edit_command_ = command_id;
1627 ////////////////////////////////////////////////////////////////////////////////
1628 // Textfield, protected:
1630 void Textfield::DoInsertChar(base::char16 ch) {
1631 OnBeforeUserAction();
1632 skip_input_method_cancel_composition_ = true;
1633 if (GetRenderText()->insert_mode())
1634 model_->InsertChar(ch);
1635 else
1636 model_->ReplaceChar(ch);
1637 skip_input_method_cancel_composition_ = false;
1639 UpdateAfterChange(true, true);
1640 OnAfterUserAction();
1643 gfx::RenderText* Textfield::GetRenderText() const {
1644 return model_->render_text();
1647 base::string16 Textfield::GetSelectionClipboardText() const {
1648 base::string16 selection_clipboard_text;
1649 ui::Clipboard::GetForCurrentThread()->ReadText(
1650 ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text);
1651 return selection_clipboard_text;
1654 ////////////////////////////////////////////////////////////////////////////////
1655 // Textfield, private:
1657 void Textfield::AccessibilitySetValue(const base::string16& new_value) {
1658 if (!read_only()) {
1659 SetText(new_value);
1660 ClearSelection();
1664 void Textfield::UpdateBackgroundColor() {
1665 const SkColor color = GetBackgroundColor();
1666 set_background(Background::CreateSolidBackground(color));
1667 // Disable subpixel rendering when the background color is transparent
1668 // because it draws incorrect colors around the glyphs in that case.
1669 // See crbug.com/115198
1670 GetRenderText()->set_subpixel_rendering_suppressed(
1671 SkColorGetA(color) != 0xFF);
1672 SchedulePaint();
1675 void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) {
1676 if (text_changed) {
1677 if (controller_)
1678 controller_->ContentsChanged(this, text());
1679 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
1681 if (cursor_changed) {
1682 cursor_visible_ = true;
1683 RepaintCursor();
1684 if (cursor_repaint_timer_.IsRunning())
1685 cursor_repaint_timer_.Reset();
1686 if (!text_changed) {
1687 // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire
1688 // this if only the selection changed.
1689 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED, true);
1692 if (text_changed || cursor_changed) {
1693 OnCaretBoundsChanged();
1694 SchedulePaint();
1698 void Textfield::UpdateCursor() {
1699 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1700 cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0);
1701 RepaintCursor();
1704 void Textfield::RepaintCursor() {
1705 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds());
1706 r.Inset(-1, -1, -1, -1);
1707 SchedulePaintInRect(r);
1710 void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
1711 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
1712 canvas->Save();
1714 // Draw placeholder text if needed.
1715 gfx::RenderText* render_text = GetRenderText();
1716 if (text().empty() && !GetPlaceholderText().empty()) {
1717 canvas->DrawStringRect(GetPlaceholderText(), GetFontList(),
1718 placeholder_text_color(), render_text->display_rect());
1721 // Draw the text, cursor, and selection.
1722 render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ &&
1723 !HasSelection());
1724 render_text->Draw(canvas);
1726 // Draw the detached drop cursor that marks where the text will be dropped.
1727 if (drop_cursor_visible_)
1728 render_text->DrawCursor(canvas, drop_cursor_position_);
1730 canvas->Restore();
1733 void Textfield::MoveCursorTo(const gfx::Point& point, bool select) {
1734 if (model_->MoveCursorTo(point, select))
1735 UpdateAfterChange(false, true);
1738 void Textfield::SelectThroughLastDragLocation() {
1739 OnBeforeUserAction();
1740 model_->MoveCursorTo(last_drag_location_, true);
1741 if (aggregated_clicks_ == 1) {
1742 model_->SelectWord();
1743 // Expand the selection so the initially selected word remains selected.
1744 gfx::Range selection = GetRenderText()->selection();
1745 const size_t min = std::min(selection.GetMin(),
1746 double_click_word_.GetMin());
1747 const size_t max = std::max(selection.GetMax(),
1748 double_click_word_.GetMax());
1749 const bool reversed = selection.is_reversed();
1750 selection.set_start(reversed ? max : min);
1751 selection.set_end(reversed ? min : max);
1752 model_->SelectRange(selection);
1754 UpdateAfterChange(false, true);
1755 OnAfterUserAction();
1758 void Textfield::OnCaretBoundsChanged() {
1759 if (GetInputMethod())
1760 GetInputMethod()->OnCaretBoundsChanged(this);
1761 if (touch_selection_controller_)
1762 touch_selection_controller_->SelectionChanged();
1765 void Textfield::OnBeforeUserAction() {
1766 DCHECK(!performing_user_action_);
1767 performing_user_action_ = true;
1768 if (controller_)
1769 controller_->OnBeforeUserAction(this);
1772 void Textfield::OnAfterUserAction() {
1773 if (controller_)
1774 controller_->OnAfterUserAction(this);
1775 DCHECK(performing_user_action_);
1776 performing_user_action_ = false;
1779 bool Textfield::Cut() {
1780 if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD &&
1781 model_->Cut()) {
1782 if (controller_)
1783 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1784 return true;
1786 return false;
1789 bool Textfield::Copy() {
1790 if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) {
1791 if (controller_)
1792 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1793 return true;
1795 return false;
1798 bool Textfield::Paste() {
1799 if (!read_only() && model_->Paste()) {
1800 if (controller_)
1801 controller_->OnAfterPaste();
1802 return true;
1804 return false;
1807 void Textfield::UpdateContextMenu() {
1808 if (!context_menu_contents_.get()) {
1809 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1810 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1811 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1812 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1813 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1814 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1815 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
1816 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1817 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1818 IDS_APP_SELECT_ALL);
1819 if (controller_)
1820 controller_->UpdateContextMenu(context_menu_contents_.get());
1822 context_menu_runner_.reset(
1823 new MenuRunner(context_menu_contents_.get(),
1824 MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU));
1827 void Textfield::TrackMouseClicks(const ui::MouseEvent& event) {
1828 if (event.IsOnlyLeftMouseButton()) {
1829 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
1830 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
1831 !ExceededDragThreshold(event.location() - last_click_location_)) {
1832 // Upon clicking after a triple click, the count should go back to double
1833 // click and alternate between double and triple. This assignment maps
1834 // 0 to 1, 1 to 2, 2 to 1.
1835 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
1836 } else {
1837 aggregated_clicks_ = 0;
1839 last_click_time_ = event.time_stamp();
1840 last_click_location_ = event.location();
1844 bool Textfield::ImeEditingAllowed() const {
1845 // Disallow input method editing of password fields.
1846 ui::TextInputType t = GetTextInputType();
1847 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
1850 void Textfield::RevealPasswordChar(int index) {
1851 GetRenderText()->SetObscuredRevealIndex(index);
1852 SchedulePaint();
1854 if (index != -1) {
1855 password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_,
1856 base::Bind(&Textfield::RevealPasswordChar,
1857 weak_ptr_factory_.GetWeakPtr(), -1));
1861 void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
1862 if (!HasFocus())
1863 return;
1865 if (!touch_selection_controller_) {
1866 touch_selection_controller_.reset(
1867 ui::TouchEditingControllerDeprecated::Create(this));
1869 if (touch_selection_controller_)
1870 touch_selection_controller_->SelectionChanged();
1873 void Textfield::UpdateSelectionClipboard() const {
1874 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1875 if (performing_user_action_ && HasSelection()) {
1876 ui::ScopedClipboardWriter(
1877 ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText());
1878 if (controller_)
1879 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION);
1881 #endif
1884 void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) {
1885 DCHECK(event.IsOnlyMiddleMouseButton());
1886 DCHECK(!read_only());
1887 base::string16 selection_clipboard_text = GetSelectionClipboardText();
1888 OnBeforeUserAction();
1889 const gfx::SelectionModel mouse =
1890 GetRenderText()->FindCursorPosition(event.location());
1891 if (!HasFocus())
1892 RequestFocus();
1893 model_->MoveCursorTo(mouse);
1894 if (!selection_clipboard_text.empty()) {
1895 model_->InsertText(selection_clipboard_text);
1896 UpdateAfterChange(true, true);
1898 OnAfterUserAction();
1901 } // namespace views