Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / ui / views / controls / textfield / textfield.cc
blobd3b684c449f1c989f45422f70df5cbfbeb23bc7b
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/base_event_utils.h"
21 #include "ui/events/event.h"
22 #include "ui/events/keycodes/keyboard_codes.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/display.h"
25 #include "ui/gfx/geometry/insets.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/native_theme/native_theme.h"
28 #include "ui/strings/grit/ui_strings.h"
29 #include "ui/views/background.h"
30 #include "ui/views/controls/focusable_border.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/controls/menu/menu_runner.h"
33 #include "ui/views/controls/native/native_view_host.h"
34 #include "ui/views/controls/textfield/textfield_controller.h"
35 #include "ui/views/drag_utils.h"
36 #include "ui/views/metrics.h"
37 #include "ui/views/native_cursor.h"
38 #include "ui/views/painter.h"
39 #include "ui/views/views_delegate.h"
40 #include "ui/views/widget/widget.h"
42 #if defined(OS_WIN)
43 #include "base/win/win_util.h"
44 #endif
46 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
47 #include "base/strings/utf_string_conversions.h"
48 #include "ui/events/linux/text_edit_command_auralinux.h"
49 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
50 #endif
52 namespace views {
54 namespace {
56 // Default placeholder text color.
57 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
59 const int kNoCommand = 0;
61 void ConvertRectToScreen(const View* src, gfx::Rect* r) {
62 DCHECK(src);
64 gfx::Point new_origin = r->origin();
65 View::ConvertPointToScreen(src, &new_origin);
66 r->set_origin(new_origin);
69 // Get the drag selection timer delay, respecting animation scaling for testing.
70 int GetDragSelectionDelay() {
71 switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) {
72 case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION: return 100;
73 case ui::ScopedAnimationDurationScaleMode::FAST_DURATION: return 25;
74 case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION: return 400;
75 case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION: return 1;
76 case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION: return 0;
78 return 100;
81 // Get the default command for a given key |event| and selection state.
82 int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) {
83 if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
84 return kNoCommand;
86 const bool shift = event.IsShiftDown();
87 const bool control = event.IsControlDown();
88 const bool alt = event.IsAltDown() || event.IsAltGrDown();
89 switch (event.key_code()) {
90 case ui::VKEY_Z:
91 if (control && !shift && !alt)
92 return IDS_APP_UNDO;
93 return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand;
94 case ui::VKEY_Y:
95 return (control && !alt) ? IDS_APP_REDO : kNoCommand;
96 case ui::VKEY_A:
97 return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand;
98 case ui::VKEY_X:
99 return (control && !alt) ? IDS_APP_CUT : kNoCommand;
100 case ui::VKEY_C:
101 return (control && !alt) ? IDS_APP_COPY : kNoCommand;
102 case ui::VKEY_V:
103 return (control && !alt) ? IDS_APP_PASTE : kNoCommand;
104 case ui::VKEY_RIGHT:
105 // Ignore alt+right, which may be a browser navigation shortcut.
106 if (alt)
107 return kNoCommand;
108 if (!shift)
109 return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT;
110 return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
111 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION;
112 case ui::VKEY_LEFT:
113 // Ignore alt+left, which may be a browser navigation shortcut.
114 if (alt)
115 return kNoCommand;
116 if (!shift)
117 return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT;
118 return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
119 IDS_MOVE_LEFT_AND_MODIFY_SELECTION;
120 case ui::VKEY_HOME:
121 return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
122 IDS_MOVE_TO_BEGINNING_OF_LINE;
123 case ui::VKEY_END:
124 return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
125 IDS_MOVE_TO_END_OF_LINE;
126 case ui::VKEY_BACK:
127 if (!control || has_selection)
128 return IDS_DELETE_BACKWARD;
129 #if defined(OS_LINUX)
130 // Only erase by line break on Linux and ChromeOS.
131 if (shift)
132 return IDS_DELETE_TO_BEGINNING_OF_LINE;
133 #endif
134 return IDS_DELETE_WORD_BACKWARD;
135 case ui::VKEY_DELETE:
136 if (!control || has_selection)
137 return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD;
138 #if defined(OS_LINUX)
139 // Only erase by line break on Linux and ChromeOS.
140 if (shift)
141 return IDS_DELETE_TO_END_OF_LINE;
142 #endif
143 return IDS_DELETE_WORD_FORWARD;
144 case ui::VKEY_INSERT:
145 if (control && !shift)
146 return IDS_APP_COPY;
147 return (shift && !control) ? IDS_APP_PASTE : kNoCommand;
148 default:
149 return kNoCommand;
153 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
154 // Convert a custom text edit |command| to the equivalent views command ID.
155 int GetViewsCommand(const ui::TextEditCommandAuraLinux& command, bool rtl) {
156 const bool select = command.extend_selection();
157 switch (command.command_id()) {
158 case ui::TextEditCommandAuraLinux::COPY:
159 return IDS_APP_COPY;
160 case ui::TextEditCommandAuraLinux::CUT:
161 return IDS_APP_CUT;
162 case ui::TextEditCommandAuraLinux::DELETE_BACKWARD:
163 return IDS_DELETE_BACKWARD;
164 case ui::TextEditCommandAuraLinux::DELETE_FORWARD:
165 return IDS_DELETE_FORWARD;
166 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE:
167 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH:
168 return IDS_DELETE_TO_BEGINNING_OF_LINE;
169 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE:
170 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH:
171 return IDS_DELETE_TO_END_OF_LINE;
172 case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD:
173 return IDS_DELETE_WORD_BACKWARD;
174 case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD:
175 return IDS_DELETE_WORD_FORWARD;
176 case ui::TextEditCommandAuraLinux::INSERT_TEXT:
177 return kNoCommand;
178 case ui::TextEditCommandAuraLinux::MOVE_BACKWARD:
179 if (rtl)
180 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
181 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
182 case ui::TextEditCommandAuraLinux::MOVE_DOWN:
183 return IDS_MOVE_DOWN;
184 case ui::TextEditCommandAuraLinux::MOVE_FORWARD:
185 if (rtl)
186 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
187 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
188 case ui::TextEditCommandAuraLinux::MOVE_LEFT:
189 return select ? IDS_MOVE_LEFT_AND_MODIFY_SELECTION : IDS_MOVE_LEFT;
190 case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN:
191 case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP:
192 return kNoCommand;
193 case ui::TextEditCommandAuraLinux::MOVE_RIGHT:
194 return select ? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION : IDS_MOVE_RIGHT;
195 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT:
196 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE:
197 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH:
198 return select ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
199 IDS_MOVE_TO_BEGINNING_OF_LINE;
200 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT:
201 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE:
202 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH:
203 return select ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
204 IDS_MOVE_TO_END_OF_LINE;
205 case ui::TextEditCommandAuraLinux::MOVE_UP:
206 return IDS_MOVE_UP;
207 case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD:
208 if (rtl) {
209 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
210 IDS_MOVE_WORD_RIGHT;
212 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
213 IDS_MOVE_WORD_LEFT;
214 case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD:
215 if (rtl) {
216 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
217 IDS_MOVE_WORD_LEFT;
219 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
220 IDS_MOVE_WORD_RIGHT;
221 case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT:
222 return select ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
223 IDS_MOVE_WORD_LEFT;
224 case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT:
225 return select ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
226 IDS_MOVE_WORD_RIGHT;
227 case ui::TextEditCommandAuraLinux::PASTE:
228 return IDS_APP_PASTE;
229 case ui::TextEditCommandAuraLinux::SELECT_ALL:
230 return IDS_APP_SELECT_ALL;
231 case ui::TextEditCommandAuraLinux::SET_MARK:
232 case ui::TextEditCommandAuraLinux::UNSELECT:
233 case ui::TextEditCommandAuraLinux::INVALID_COMMAND:
234 return kNoCommand;
236 return kNoCommand;
238 #endif
240 } // namespace
242 // static
243 const char Textfield::kViewClassName[] = "Textfield";
244 const int Textfield::kTextPadding = 3;
246 // static
247 size_t Textfield::GetCaretBlinkMs() {
248 static const size_t default_value = 500;
249 #if defined(OS_WIN)
250 static const size_t system_value = ::GetCaretBlinkTime();
251 if (system_value != 0)
252 return (system_value == INFINITE) ? 0 : system_value;
253 #endif
254 return default_value;
257 Textfield::Textfield()
258 : model_(new TextfieldModel(this)),
259 controller_(NULL),
260 scheduled_edit_command_(kNoCommand),
261 read_only_(false),
262 default_width_in_chars_(0),
263 use_default_text_color_(true),
264 use_default_background_color_(true),
265 use_default_selection_text_color_(true),
266 use_default_selection_background_color_(true),
267 text_color_(SK_ColorBLACK),
268 background_color_(SK_ColorWHITE),
269 selection_text_color_(SK_ColorWHITE),
270 selection_background_color_(SK_ColorBLUE),
271 placeholder_text_color_(kDefaultPlaceholderTextColor),
272 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
273 text_input_flags_(0),
274 performing_user_action_(false),
275 skip_input_method_cancel_composition_(false),
276 cursor_visible_(false),
277 drop_cursor_visible_(false),
278 initiating_drag_(false),
279 aggregated_clicks_(0),
280 drag_start_display_offset_(0),
281 touch_handles_hidden_due_to_scroll_(false),
282 weak_ptr_factory_(this) {
283 set_context_menu_controller(this);
284 set_drag_controller(this);
285 SetBorder(scoped_ptr<Border>(new FocusableBorder()));
286 SetFocusable(true);
288 if (ViewsDelegate::GetInstance()) {
289 password_reveal_duration_ =
290 ViewsDelegate::GetInstance()
291 ->GetDefaultTextfieldObscuredRevealDuration();
294 // These allow BrowserView to pass edit commands from the Chrome menu to us
295 // when we're focused by simply asking the FocusManager to
296 // ProcessAccelerator() with the relevant accelerators.
297 AddAccelerator(ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN));
298 AddAccelerator(ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN));
299 AddAccelerator(ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN));
302 Textfield::~Textfield() {
303 if (GetInputMethod()) {
304 // The textfield should have been blurred before destroy.
305 DCHECK(this != GetInputMethod()->GetTextInputClient());
309 void Textfield::SetReadOnly(bool read_only) {
310 // Update read-only without changing the focusable state (or active, etc.).
311 read_only_ = read_only;
312 if (GetInputMethod())
313 GetInputMethod()->OnTextInputTypeChanged(this);
314 SetColor(GetTextColor());
315 UpdateBackgroundColor();
318 void Textfield::SetTextInputType(ui::TextInputType type) {
319 GetRenderText()->SetObscured(type == ui::TEXT_INPUT_TYPE_PASSWORD);
320 text_input_type_ = type;
321 OnCaretBoundsChanged();
322 if (GetInputMethod())
323 GetInputMethod()->OnTextInputTypeChanged(this);
324 SchedulePaint();
327 void Textfield::SetTextInputFlags(int flags) {
328 text_input_flags_ = flags;
331 void Textfield::SetText(const base::string16& new_text) {
332 model_->SetText(new_text);
333 OnCaretBoundsChanged();
334 SchedulePaint();
335 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
338 void Textfield::AppendText(const base::string16& new_text) {
339 if (new_text.empty())
340 return;
341 model_->Append(new_text);
342 OnCaretBoundsChanged();
343 SchedulePaint();
346 void Textfield::InsertOrReplaceText(const base::string16& new_text) {
347 if (new_text.empty())
348 return;
349 model_->InsertText(new_text);
350 OnCaretBoundsChanged();
351 SchedulePaint();
354 base::i18n::TextDirection Textfield::GetTextDirection() const {
355 return GetRenderText()->GetDisplayTextDirection();
358 base::string16 Textfield::GetSelectedText() const {
359 return model_->GetSelectedText();
362 void Textfield::SelectAll(bool reversed) {
363 model_->SelectAll(reversed);
364 UpdateSelectionClipboard();
365 UpdateAfterChange(false, true);
368 void Textfield::SelectWordAt(const gfx::Point& point) {
369 model_->MoveCursorTo(point, false);
370 model_->SelectWord();
371 UpdateAfterChange(false, true);
374 void Textfield::ClearSelection() {
375 model_->ClearSelection();
376 UpdateAfterChange(false, true);
379 bool Textfield::HasSelection() const {
380 return !GetSelectedRange().is_empty();
383 SkColor Textfield::GetTextColor() const {
384 if (!use_default_text_color_)
385 return text_color_;
387 return GetNativeTheme()->GetSystemColor(read_only() ?
388 ui::NativeTheme::kColorId_TextfieldReadOnlyColor :
389 ui::NativeTheme::kColorId_TextfieldDefaultColor);
392 void Textfield::SetTextColor(SkColor color) {
393 text_color_ = color;
394 use_default_text_color_ = false;
395 SetColor(color);
398 void Textfield::UseDefaultTextColor() {
399 use_default_text_color_ = true;
400 SetColor(GetTextColor());
403 SkColor Textfield::GetBackgroundColor() const {
404 if (!use_default_background_color_)
405 return background_color_;
407 return GetNativeTheme()->GetSystemColor(read_only() ?
408 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground :
409 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
412 void Textfield::SetBackgroundColor(SkColor color) {
413 background_color_ = color;
414 use_default_background_color_ = false;
415 UpdateBackgroundColor();
418 void Textfield::UseDefaultBackgroundColor() {
419 use_default_background_color_ = true;
420 UpdateBackgroundColor();
423 SkColor Textfield::GetSelectionTextColor() const {
424 return use_default_selection_text_color_ ?
425 GetNativeTheme()->GetSystemColor(
426 ui::NativeTheme::kColorId_TextfieldSelectionColor) :
427 selection_text_color_;
430 void Textfield::SetSelectionTextColor(SkColor color) {
431 selection_text_color_ = color;
432 use_default_selection_text_color_ = false;
433 GetRenderText()->set_selection_color(GetSelectionTextColor());
434 SchedulePaint();
437 void Textfield::UseDefaultSelectionTextColor() {
438 use_default_selection_text_color_ = true;
439 GetRenderText()->set_selection_color(GetSelectionTextColor());
440 SchedulePaint();
443 void Textfield::SetShadows(const gfx::ShadowValues& shadows) {
444 GetRenderText()->set_shadows(shadows);
445 SchedulePaint();
448 SkColor Textfield::GetSelectionBackgroundColor() const {
449 return use_default_selection_background_color_ ?
450 GetNativeTheme()->GetSystemColor(
451 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused) :
452 selection_background_color_;
455 void Textfield::SetSelectionBackgroundColor(SkColor color) {
456 selection_background_color_ = color;
457 use_default_selection_background_color_ = false;
458 GetRenderText()->set_selection_background_focused_color(
459 GetSelectionBackgroundColor());
460 SchedulePaint();
463 void Textfield::UseDefaultSelectionBackgroundColor() {
464 use_default_selection_background_color_ = true;
465 GetRenderText()->set_selection_background_focused_color(
466 GetSelectionBackgroundColor());
467 SchedulePaint();
470 bool Textfield::GetCursorEnabled() const {
471 return GetRenderText()->cursor_enabled();
474 void Textfield::SetCursorEnabled(bool enabled) {
475 GetRenderText()->SetCursorEnabled(enabled);
478 const gfx::FontList& Textfield::GetFontList() const {
479 return GetRenderText()->font_list();
482 void Textfield::SetFontList(const gfx::FontList& font_list) {
483 GetRenderText()->SetFontList(font_list);
484 OnCaretBoundsChanged();
485 PreferredSizeChanged();
488 base::string16 Textfield::GetPlaceholderText() const {
489 return placeholder_text_;
492 gfx::HorizontalAlignment Textfield::GetHorizontalAlignment() const {
493 return GetRenderText()->horizontal_alignment();
496 void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
497 GetRenderText()->SetHorizontalAlignment(alignment);
500 void Textfield::ShowImeIfNeeded() {
501 if (enabled() && !read_only())
502 GetInputMethod()->ShowImeIfNeeded();
505 bool Textfield::IsIMEComposing() const {
506 return model_->HasCompositionText();
509 const gfx::Range& Textfield::GetSelectedRange() const {
510 return GetRenderText()->selection();
513 void Textfield::SelectRange(const gfx::Range& range) {
514 model_->SelectRange(range);
515 UpdateAfterChange(false, true);
518 const gfx::SelectionModel& Textfield::GetSelectionModel() const {
519 return GetRenderText()->selection_model();
522 void Textfield::SelectSelectionModel(const gfx::SelectionModel& sel) {
523 model_->SelectSelectionModel(sel);
524 UpdateAfterChange(false, true);
527 size_t Textfield::GetCursorPosition() const {
528 return model_->GetCursorPosition();
531 void Textfield::SetColor(SkColor value) {
532 GetRenderText()->SetColor(value);
533 SchedulePaint();
536 void Textfield::ApplyColor(SkColor value, const gfx::Range& range) {
537 GetRenderText()->ApplyColor(value, range);
538 SchedulePaint();
541 void Textfield::SetStyle(gfx::TextStyle style, bool value) {
542 GetRenderText()->SetStyle(style, value);
543 SchedulePaint();
546 void Textfield::ApplyStyle(gfx::TextStyle style,
547 bool value,
548 const gfx::Range& range) {
549 GetRenderText()->ApplyStyle(style, value, range);
550 SchedulePaint();
553 void Textfield::ClearEditHistory() {
554 model_->ClearEditHistory();
557 void Textfield::SetAccessibleName(const base::string16& name) {
558 accessible_name_ = name;
561 void Textfield::ExecuteCommand(int command_id) {
562 ExecuteCommand(command_id, ui::EF_NONE);
565 void Textfield::SetFocusPainter(scoped_ptr<Painter> focus_painter) {
566 focus_painter_ = focus_painter.Pass();
569 bool Textfield::HasTextBeingDragged() {
570 return initiating_drag_;
573 ////////////////////////////////////////////////////////////////////////////////
574 // Textfield, View overrides:
576 gfx::Insets Textfield::GetInsets() const {
577 gfx::Insets insets = View::GetInsets();
578 insets += gfx::Insets(kTextPadding, kTextPadding, kTextPadding, kTextPadding);
579 return insets;
582 int Textfield::GetBaseline() const {
583 return GetInsets().top() + GetRenderText()->GetBaseline();
586 gfx::Size Textfield::GetPreferredSize() const {
587 const gfx::Insets& insets = GetInsets();
588 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_) +
589 insets.width(), GetFontList().GetHeight() + insets.height());
592 const char* Textfield::GetClassName() const {
593 return kViewClassName;
596 gfx::NativeCursor Textfield::GetCursor(const ui::MouseEvent& event) {
597 bool in_selection = GetRenderText()->IsPointInSelection(event.location());
598 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
599 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
600 return text_cursor ? GetNativeIBeamCursor() : gfx::kNullCursor;
603 bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
604 TrackMouseClicks(event);
606 if (!controller_ || !controller_->HandleMouseEvent(this, event)) {
607 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) {
608 RequestFocus();
609 ShowImeIfNeeded();
612 if (event.IsOnlyLeftMouseButton()) {
613 OnBeforeUserAction();
614 initiating_drag_ = false;
615 switch (aggregated_clicks_) {
616 case 0:
617 if (GetRenderText()->IsPointInSelection(event.location()))
618 initiating_drag_ = true;
619 else
620 MoveCursorTo(event.location(), event.IsShiftDown());
621 break;
622 case 1:
623 SelectWordAt(event.location());
624 double_click_word_ = GetRenderText()->selection();
625 break;
626 case 2:
627 SelectAll(false);
628 break;
629 default:
630 NOTREACHED();
632 OnAfterUserAction();
635 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
636 if (event.IsOnlyMiddleMouseButton()) {
637 if (GetRenderText()->IsPointInSelection(event.location())) {
638 OnBeforeUserAction();
639 ClearSelection();
640 ui::ScopedClipboardWriter(
641 ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16());
642 OnAfterUserAction();
643 } else if (!read_only()) {
644 PasteSelectionClipboard(event);
647 #endif
650 return true;
653 bool Textfield::OnMouseDragged(const ui::MouseEvent& event) {
654 last_drag_location_ = event.location();
656 // Don't adjust the cursor on a potential drag and drop, or if the mouse
657 // movement from the last mouse click does not exceed the drag threshold.
658 if (initiating_drag_ || !event.IsOnlyLeftMouseButton() ||
659 !ExceededDragThreshold(last_drag_location_ - last_click_location_)) {
660 return true;
663 // A timer is used to continuously scroll while selecting beyond side edges.
664 const int x = event.location().x();
665 if ((x >= 0 && x <= width()) || GetDragSelectionDelay() == 0) {
666 drag_selection_timer_.Stop();
667 SelectThroughLastDragLocation();
668 } else if (!drag_selection_timer_.IsRunning()) {
669 // Select through the edge of the visible text, then start the scroll timer.
670 last_drag_location_.set_x(std::min(std::max(0, x), width()));
671 SelectThroughLastDragLocation();
672 drag_selection_timer_.Start(
673 FROM_HERE, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()),
674 this, &Textfield::SelectThroughLastDragLocation);
677 return true;
680 void Textfield::OnMouseReleased(const ui::MouseEvent& event) {
681 OnBeforeUserAction();
682 drag_selection_timer_.Stop();
683 // Cancel suspected drag initiations, the user was clicking in the selection.
684 if (initiating_drag_)
685 MoveCursorTo(event.location(), false);
686 initiating_drag_ = false;
687 UpdateSelectionClipboard();
688 OnAfterUserAction();
691 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
692 int edit_command = scheduled_edit_command_;
693 scheduled_edit_command_ = kNoCommand;
695 // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify
696 // it isn't null before proceeding.
697 base::WeakPtr<Textfield> textfield(weak_ptr_factory_.GetWeakPtr());
699 bool handled = controller_ && controller_->HandleKeyEvent(this, event);
701 if (!textfield)
702 return handled;
704 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
705 ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
706 ui::GetTextEditKeyBindingsDelegate();
707 std::vector<ui::TextEditCommandAuraLinux> commands;
708 if (!handled && delegate && delegate->MatchEvent(event, &commands)) {
709 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
710 for (size_t i = 0; i < commands.size(); ++i) {
711 const int command = GetViewsCommand(commands[i], rtl);
712 if (IsCommandIdEnabled(command)) {
713 ExecuteCommand(command);
714 handled = true;
717 return handled;
719 #endif
721 if (edit_command == kNoCommand)
722 edit_command = GetCommandForKeyEvent(event, HasSelection());
724 if (!handled && IsCommandIdEnabled(edit_command)) {
725 ExecuteCommand(edit_command);
726 handled = true;
728 return handled;
731 void Textfield::OnGestureEvent(ui::GestureEvent* event) {
732 switch (event->type()) {
733 case ui::ET_GESTURE_TAP_DOWN:
734 RequestFocus();
735 ShowImeIfNeeded();
736 event->SetHandled();
737 break;
738 case ui::ET_GESTURE_TAP:
739 if (event->details().tap_count() == 1) {
740 // If tap is on the selection and touch handles are not present, handles
741 // should be shown without changing selection. Otherwise, cursor should
742 // be moved to the tap location.
743 if (touch_selection_controller_ ||
744 !GetRenderText()->IsPointInSelection(event->location())) {
745 OnBeforeUserAction();
746 MoveCursorTo(event->location(), false);
747 OnAfterUserAction();
749 } else if (event->details().tap_count() == 2) {
750 OnBeforeUserAction();
751 SelectWordAt(event->location());
752 OnAfterUserAction();
753 } else {
754 OnBeforeUserAction();
755 SelectAll(false);
756 OnAfterUserAction();
758 CreateTouchSelectionControllerAndNotifyIt();
759 #if defined(OS_WIN)
760 if (!read_only())
761 base::win::DisplayVirtualKeyboard();
762 #endif
763 event->SetHandled();
764 break;
765 case ui::ET_GESTURE_LONG_PRESS:
766 if (!GetRenderText()->IsPointInSelection(event->location())) {
767 // If long-press happens outside selection, select word and try to
768 // activate touch selection.
769 OnBeforeUserAction();
770 SelectWordAt(event->location());
771 OnAfterUserAction();
772 CreateTouchSelectionControllerAndNotifyIt();
773 // If touch selection activated successfully, mark event as handled so
774 // that the regular context menu is not shown.
775 if (touch_selection_controller_)
776 event->SetHandled();
777 } else {
778 // If long-press happens on the selection, deactivate touch selection
779 // and try to initiate drag-drop. If drag-drop is not enabled, context
780 // menu will be shown. Event is not marked as handled to let Views
781 // handle drag-drop or context menu.
782 DestroyTouchSelection();
783 initiating_drag_ = switches::IsTouchDragDropEnabled();
785 break;
786 case ui::ET_GESTURE_LONG_TAP:
787 // If touch selection is enabled, the context menu on long tap will be
788 // shown by the |touch_selection_controller_|, hence we mark the event
789 // handled so Views does not try to show context menu on it.
790 if (touch_selection_controller_)
791 event->SetHandled();
792 break;
793 case ui::ET_GESTURE_SCROLL_BEGIN:
794 touch_handles_hidden_due_to_scroll_ = touch_selection_controller_ != NULL;
795 DestroyTouchSelection();
796 drag_start_location_ = event->location();
797 drag_start_display_offset_ =
798 GetRenderText()->GetUpdatedDisplayOffset().x();
799 event->SetHandled();
800 break;
801 case ui::ET_GESTURE_SCROLL_UPDATE: {
802 int new_offset = drag_start_display_offset_ + event->location().x() -
803 drag_start_location_.x();
804 GetRenderText()->SetDisplayOffset(new_offset);
805 SchedulePaint();
806 event->SetHandled();
807 break;
809 case ui::ET_GESTURE_SCROLL_END:
810 case ui::ET_SCROLL_FLING_START:
811 if (touch_handles_hidden_due_to_scroll_) {
812 CreateTouchSelectionControllerAndNotifyIt();
813 touch_handles_hidden_due_to_scroll_ = false;
815 event->SetHandled();
816 break;
817 default:
818 return;
822 // This function is called by BrowserView to execute clipboard commands.
823 bool Textfield::AcceleratorPressed(const ui::Accelerator& accelerator) {
824 ui::KeyEvent event(accelerator.type(), accelerator.key_code(),
825 accelerator.modifiers());
826 ExecuteCommand(GetCommandForKeyEvent(event, HasSelection()));
827 return true;
830 bool Textfield::CanHandleAccelerators() const {
831 return GetRenderText()->focused() && View::CanHandleAccelerators();
834 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
835 SelectAll(false);
838 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
839 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
840 // Skip any accelerator handling that conflicts with custom keybindings.
841 ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
842 ui::GetTextEditKeyBindingsDelegate();
843 std::vector<ui::TextEditCommandAuraLinux> commands;
844 if (delegate && delegate->MatchEvent(event, &commands)) {
845 const bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
846 for (size_t i = 0; i < commands.size(); ++i)
847 if (IsCommandIdEnabled(GetViewsCommand(commands[i], rtl)))
848 return true;
850 #endif
852 // Skip backspace accelerator handling; editable textfields handle this key.
853 // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes.
854 const bool is_backspace = event.key_code() == ui::VKEY_BACK;
855 return (is_backspace && !read_only()) || event.IsUnicodeKeyCode();
858 bool Textfield::GetDropFormats(
859 int* formats,
860 std::set<OSExchangeData::CustomFormat>* custom_formats) {
861 if (!enabled() || read_only())
862 return false;
863 // TODO(msw): Can we support URL, FILENAME, etc.?
864 *formats = ui::OSExchangeData::STRING;
865 if (controller_)
866 controller_->AppendDropFormats(formats, custom_formats);
867 return true;
870 bool Textfield::CanDrop(const OSExchangeData& data) {
871 int formats;
872 std::set<OSExchangeData::CustomFormat> custom_formats;
873 GetDropFormats(&formats, &custom_formats);
874 return enabled() && !read_only() &&
875 data.HasAnyFormat(formats, custom_formats);
878 int Textfield::OnDragUpdated(const ui::DropTargetEvent& event) {
879 DCHECK(CanDrop(event.data()));
880 gfx::RenderText* render_text = GetRenderText();
881 const gfx::Range& selection = render_text->selection();
882 drop_cursor_position_ = render_text->FindCursorPosition(event.location());
883 bool in_selection = !selection.is_empty() &&
884 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos()));
885 drop_cursor_visible_ = !in_selection;
886 // TODO(msw): Pan over text when the user drags to the visible text edge.
887 OnCaretBoundsChanged();
888 SchedulePaint();
890 if (initiating_drag_) {
891 if (in_selection)
892 return ui::DragDropTypes::DRAG_NONE;
893 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
894 ui::DragDropTypes::DRAG_MOVE;
896 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
899 void Textfield::OnDragExited() {
900 drop_cursor_visible_ = false;
901 SchedulePaint();
904 int Textfield::OnPerformDrop(const ui::DropTargetEvent& event) {
905 DCHECK(CanDrop(event.data()));
906 drop_cursor_visible_ = false;
908 if (controller_) {
909 int drag_operation = controller_->OnDrop(event.data());
910 if (drag_operation != ui::DragDropTypes::DRAG_NONE)
911 return drag_operation;
914 gfx::RenderText* render_text = GetRenderText();
915 DCHECK(!initiating_drag_ ||
916 !render_text->IsPointInSelection(event.location()));
917 OnBeforeUserAction();
918 skip_input_method_cancel_composition_ = true;
920 gfx::SelectionModel drop_destination_model =
921 render_text->FindCursorPosition(event.location());
922 base::string16 new_text;
923 event.data().GetString(&new_text);
925 // Delete the current selection for a drag and drop within this view.
926 const bool move = initiating_drag_ && !event.IsControlDown() &&
927 event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
928 if (move) {
929 // Adjust the drop destination if it is on or after the current selection.
930 size_t pos = drop_destination_model.caret_pos();
931 pos -= render_text->selection().Intersect(gfx::Range(0, pos)).length();
932 model_->DeleteSelectionAndInsertTextAt(new_text, pos);
933 } else {
934 model_->MoveCursorTo(drop_destination_model);
935 // Drop always inserts text even if the textfield is not in insert mode.
936 model_->InsertText(new_text);
938 skip_input_method_cancel_composition_ = false;
939 UpdateAfterChange(true, true);
940 OnAfterUserAction();
941 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
944 void Textfield::OnDragDone() {
945 initiating_drag_ = false;
946 drop_cursor_visible_ = false;
949 void Textfield::GetAccessibleState(ui::AXViewState* state) {
950 state->role = ui::AX_ROLE_TEXT_FIELD;
951 state->name = accessible_name_;
952 if (read_only())
953 state->AddStateFlag(ui::AX_STATE_READ_ONLY);
954 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) {
955 state->AddStateFlag(ui::AX_STATE_PROTECTED);
956 state->value = base::string16(text().size(), '*');
957 } else {
958 state->value = text();
960 const gfx::Range range = GetSelectedRange();
961 state->selection_start = range.start();
962 state->selection_end = range.end();
964 if (!read_only()) {
965 state->set_value_callback =
966 base::Bind(&Textfield::AccessibilitySetValue,
967 weak_ptr_factory_.GetWeakPtr());
971 void Textfield::OnBoundsChanged(const gfx::Rect& previous_bounds) {
972 // Textfield insets include a reasonable amount of whitespace on all sides of
973 // the default font list. Fallback fonts with larger heights may paint over
974 // the vertical whitespace as needed. Alternate solutions involve undesirable
975 // behavior like changing the default font size, shrinking some fallback fonts
976 // beyond their legibility, or enlarging controls dynamically with content.
977 gfx::Rect bounds = GetContentsBounds();
978 // GetContentsBounds() does not actually use the local GetInsets() override.
979 bounds.Inset(gfx::Insets(0, kTextPadding, 0, kTextPadding));
980 GetRenderText()->SetDisplayRect(bounds);
981 OnCaretBoundsChanged();
984 bool Textfield::GetNeedsNotificationWhenVisibleBoundsChange() const {
985 return true;
988 void Textfield::OnVisibleBoundsChanged() {
989 if (touch_selection_controller_)
990 touch_selection_controller_->SelectionChanged();
993 void Textfield::OnEnabledChanged() {
994 View::OnEnabledChanged();
995 if (GetInputMethod())
996 GetInputMethod()->OnTextInputTypeChanged(this);
997 SchedulePaint();
1000 void Textfield::OnPaint(gfx::Canvas* canvas) {
1001 OnPaintBackground(canvas);
1002 PaintTextAndCursor(canvas);
1003 OnPaintBorder(canvas);
1006 void Textfield::OnFocus() {
1007 GetRenderText()->set_focused(true);
1008 cursor_visible_ = true;
1009 SchedulePaint();
1010 if (GetInputMethod())
1011 GetInputMethod()->SetFocusedTextInputClient(this);
1012 OnCaretBoundsChanged();
1014 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1015 if (caret_blink_ms != 0) {
1016 cursor_repaint_timer_.Start(FROM_HERE,
1017 base::TimeDelta::FromMilliseconds(caret_blink_ms), this,
1018 &Textfield::UpdateCursor);
1021 View::OnFocus();
1022 SchedulePaint();
1025 void Textfield::OnBlur() {
1026 GetRenderText()->set_focused(false);
1027 if (GetInputMethod())
1028 GetInputMethod()->DetachTextInputClient(this);
1029 cursor_repaint_timer_.Stop();
1030 if (cursor_visible_) {
1031 cursor_visible_ = false;
1032 RepaintCursor();
1035 DestroyTouchSelection();
1037 // Border typically draws focus indicator.
1038 SchedulePaint();
1041 gfx::Point Textfield::GetKeyboardContextMenuLocation() {
1042 return GetCaretBounds().bottom_right();
1045 void Textfield::OnNativeThemeChanged(const ui::NativeTheme* theme) {
1046 gfx::RenderText* render_text = GetRenderText();
1047 render_text->SetColor(GetTextColor());
1048 UpdateBackgroundColor();
1049 render_text->set_cursor_color(GetTextColor());
1050 render_text->set_selection_color(GetSelectionTextColor());
1051 render_text->set_selection_background_focused_color(
1052 GetSelectionBackgroundColor());
1055 ////////////////////////////////////////////////////////////////////////////////
1056 // Textfield, TextfieldModel::Delegate overrides:
1058 void Textfield::OnCompositionTextConfirmedOrCleared() {
1059 if (!skip_input_method_cancel_composition_)
1060 GetInputMethod()->CancelComposition(this);
1063 ////////////////////////////////////////////////////////////////////////////////
1064 // Textfield, ContextMenuController overrides:
1066 void Textfield::ShowContextMenuForView(View* source,
1067 const gfx::Point& point,
1068 ui::MenuSourceType source_type) {
1069 UpdateContextMenu();
1070 ignore_result(context_menu_runner_->RunMenuAt(GetWidget(),
1071 NULL,
1072 gfx::Rect(point, gfx::Size()),
1073 MENU_ANCHOR_TOPLEFT,
1074 source_type));
1077 ////////////////////////////////////////////////////////////////////////////////
1078 // Textfield, DragController overrides:
1080 void Textfield::WriteDragDataForView(View* sender,
1081 const gfx::Point& press_pt,
1082 OSExchangeData* data) {
1083 const base::string16& selected_text(GetSelectedText());
1084 data->SetString(selected_text);
1085 Label label(selected_text, GetFontList());
1086 label.SetBackgroundColor(GetBackgroundColor());
1087 label.SetSubpixelRenderingEnabled(false);
1088 gfx::Size size(label.GetPreferredSize());
1089 gfx::NativeView native_view = GetWidget()->GetNativeView();
1090 gfx::Display display = gfx::Screen::GetScreenFor(native_view)->
1091 GetDisplayNearestWindow(native_view);
1092 size.SetToMin(gfx::Size(display.size().width(), height()));
1093 label.SetBoundsRect(gfx::Rect(size));
1094 scoped_ptr<gfx::Canvas> canvas(
1095 GetCanvasForDragImage(GetWidget(), label.size()));
1096 label.SetEnabledColor(GetTextColor());
1097 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1098 // Desktop Linux Aura does not yet support transparency in drag images.
1099 canvas->DrawColor(GetBackgroundColor());
1100 #endif
1101 label.Paint(ui::CanvasPainter(canvas.get(), 1.f).context());
1102 const gfx::Vector2d kOffset(-15, 0);
1103 drag_utils::SetDragImageOnDataObject(*canvas, kOffset, data);
1104 if (controller_)
1105 controller_->OnWriteDragData(data);
1108 int Textfield::GetDragOperationsForView(View* sender, const gfx::Point& p) {
1109 int drag_operations = ui::DragDropTypes::DRAG_COPY;
1110 if (!enabled() || text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD ||
1111 !GetRenderText()->IsPointInSelection(p)) {
1112 drag_operations = ui::DragDropTypes::DRAG_NONE;
1113 } else if (sender == this && !read_only()) {
1114 drag_operations =
1115 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
1117 if (controller_)
1118 controller_->OnGetDragOperationsForTextfield(&drag_operations);
1119 return drag_operations;
1122 bool Textfield::CanStartDragForView(View* sender,
1123 const gfx::Point& press_pt,
1124 const gfx::Point& p) {
1125 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt);
1128 ////////////////////////////////////////////////////////////////////////////////
1129 // Textfield, ui::TouchEditable overrides:
1131 void Textfield::SelectRect(const gfx::Point& start, const gfx::Point& end) {
1132 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1133 return;
1135 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start);
1136 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end);
1137 gfx::SelectionModel selection(
1138 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()),
1139 end_caret.caret_affinity());
1141 OnBeforeUserAction();
1142 SelectSelectionModel(selection);
1143 OnAfterUserAction();
1146 void Textfield::MoveCaretTo(const gfx::Point& point) {
1147 SelectRect(point, point);
1150 void Textfield::GetSelectionEndPoints(ui::SelectionBound* anchor,
1151 ui::SelectionBound* focus) {
1152 gfx::RenderText* render_text = GetRenderText();
1153 const gfx::SelectionModel& sel = render_text->selection_model();
1154 gfx::SelectionModel start_sel =
1155 render_text->GetSelectionModelForSelectionStart();
1156 gfx::Rect r1 = render_text->GetCursorBounds(start_sel, true);
1157 gfx::Rect r2 = render_text->GetCursorBounds(sel, true);
1159 anchor->SetEdge(r1.origin(), r1.bottom_left());
1160 focus->SetEdge(r2.origin(), r2.bottom_left());
1162 // Determine the SelectionBound's type for focus and anchor.
1163 // TODO(mfomitchev): Ideally we should have different logical directions for
1164 // start and end to support proper handle direction for mixed LTR/RTL text.
1165 const bool ltr = GetTextDirection() != base::i18n::RIGHT_TO_LEFT;
1166 size_t anchor_position_index = sel.selection().start();
1167 size_t focus_position_index = sel.selection().end();
1169 if (anchor_position_index == focus_position_index) {
1170 anchor->set_type(ui::SelectionBound::CENTER);
1171 focus->set_type(ui::SelectionBound::CENTER);
1172 } else if ((ltr && anchor_position_index < focus_position_index) ||
1173 (!ltr && anchor_position_index > focus_position_index)) {
1174 anchor->set_type(ui::SelectionBound::LEFT);
1175 focus->set_type(ui::SelectionBound::RIGHT);
1176 } else {
1177 anchor->set_type(ui::SelectionBound::RIGHT);
1178 focus->set_type(ui::SelectionBound::LEFT);
1182 gfx::Rect Textfield::GetBounds() {
1183 return GetLocalBounds();
1186 gfx::NativeView Textfield::GetNativeView() const {
1187 return GetWidget()->GetNativeView();
1190 void Textfield::ConvertPointToScreen(gfx::Point* point) {
1191 View::ConvertPointToScreen(this, point);
1194 void Textfield::ConvertPointFromScreen(gfx::Point* point) {
1195 View::ConvertPointFromScreen(this, point);
1198 bool Textfield::DrawsHandles() {
1199 return false;
1202 void Textfield::OpenContextMenu(const gfx::Point& anchor) {
1203 DestroyTouchSelection();
1204 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU);
1207 void Textfield::DestroyTouchSelection() {
1208 touch_selection_controller_.reset();
1211 ////////////////////////////////////////////////////////////////////////////////
1212 // Textfield, ui::SimpleMenuModel::Delegate overrides:
1214 bool Textfield::IsCommandIdChecked(int command_id) const {
1215 return true;
1218 bool Textfield::IsCommandIdEnabled(int command_id) const {
1219 base::string16 result;
1220 bool editable = !read_only();
1221 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
1222 switch (command_id) {
1223 case IDS_APP_UNDO:
1224 return editable && model_->CanUndo();
1225 case IDS_APP_REDO:
1226 return editable && model_->CanRedo();
1227 case IDS_APP_CUT:
1228 return editable && readable && model_->HasSelection();
1229 case IDS_APP_COPY:
1230 return readable && model_->HasSelection();
1231 case IDS_APP_PASTE:
1232 ui::Clipboard::GetForCurrentThread()->ReadText(
1233 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
1234 return editable && !result.empty();
1235 case IDS_APP_DELETE:
1236 return editable && model_->HasSelection();
1237 case IDS_APP_SELECT_ALL:
1238 return !text().empty();
1239 case IDS_DELETE_FORWARD:
1240 case IDS_DELETE_BACKWARD:
1241 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1242 case IDS_DELETE_TO_END_OF_LINE:
1243 case IDS_DELETE_WORD_BACKWARD:
1244 case IDS_DELETE_WORD_FORWARD:
1245 return editable;
1246 case IDS_MOVE_LEFT:
1247 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1248 case IDS_MOVE_RIGHT:
1249 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1250 case IDS_MOVE_WORD_LEFT:
1251 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1252 case IDS_MOVE_WORD_RIGHT:
1253 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1254 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1255 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1256 case IDS_MOVE_TO_END_OF_LINE:
1257 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1258 return true;
1259 default:
1260 return false;
1264 bool Textfield::GetAcceleratorForCommandId(int command_id,
1265 ui::Accelerator* accelerator) {
1266 switch (command_id) {
1267 case IDS_APP_UNDO:
1268 *accelerator = ui::Accelerator(ui::VKEY_Z, ui::EF_CONTROL_DOWN);
1269 return true;
1271 case IDS_APP_CUT:
1272 *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
1273 return true;
1275 case IDS_APP_COPY:
1276 *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
1277 return true;
1279 case IDS_APP_PASTE:
1280 *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
1281 return true;
1283 case IDS_APP_SELECT_ALL:
1284 *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN);
1285 return true;
1287 default:
1288 return false;
1292 void Textfield::ExecuteCommand(int command_id, int event_flags) {
1293 DestroyTouchSelection();
1294 if (!IsCommandIdEnabled(command_id))
1295 return;
1297 bool text_changed = false;
1298 bool cursor_changed = false;
1299 bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
1300 gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
1301 gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
1302 gfx::SelectionModel selection_model = GetSelectionModel();
1304 OnBeforeUserAction();
1305 switch (command_id) {
1306 case IDS_APP_UNDO:
1307 text_changed = cursor_changed = model_->Undo();
1308 break;
1309 case IDS_APP_REDO:
1310 text_changed = cursor_changed = model_->Redo();
1311 break;
1312 case IDS_APP_CUT:
1313 text_changed = cursor_changed = Cut();
1314 break;
1315 case IDS_APP_COPY:
1316 Copy();
1317 break;
1318 case IDS_APP_PASTE:
1319 text_changed = cursor_changed = Paste();
1320 break;
1321 case IDS_APP_DELETE:
1322 text_changed = cursor_changed = model_->Delete();
1323 break;
1324 case IDS_APP_SELECT_ALL:
1325 SelectAll(false);
1326 break;
1327 case IDS_DELETE_BACKWARD:
1328 text_changed = cursor_changed = model_->Backspace();
1329 break;
1330 case IDS_DELETE_FORWARD:
1331 text_changed = cursor_changed = model_->Delete();
1332 break;
1333 case IDS_DELETE_TO_END_OF_LINE:
1334 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1335 text_changed = cursor_changed = model_->Delete();
1336 break;
1337 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1338 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1339 text_changed = cursor_changed = model_->Backspace();
1340 break;
1341 case IDS_DELETE_WORD_BACKWARD:
1342 model_->MoveCursor(gfx::WORD_BREAK, begin, true);
1343 text_changed = cursor_changed = model_->Backspace();
1344 break;
1345 case IDS_DELETE_WORD_FORWARD:
1346 model_->MoveCursor(gfx::WORD_BREAK, end, true);
1347 text_changed = cursor_changed = model_->Delete();
1348 break;
1349 case IDS_MOVE_LEFT:
1350 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
1351 break;
1352 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1353 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
1354 break;
1355 case IDS_MOVE_RIGHT:
1356 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
1357 break;
1358 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1359 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
1360 break;
1361 case IDS_MOVE_WORD_LEFT:
1362 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
1363 break;
1364 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1365 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
1366 break;
1367 case IDS_MOVE_WORD_RIGHT:
1368 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
1369 break;
1370 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1371 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
1372 break;
1373 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1374 model_->MoveCursor(gfx::LINE_BREAK, begin, false);
1375 break;
1376 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1377 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1378 break;
1379 case IDS_MOVE_TO_END_OF_LINE:
1380 model_->MoveCursor(gfx::LINE_BREAK, end, false);
1381 break;
1382 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1383 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1384 break;
1385 default:
1386 NOTREACHED();
1387 break;
1390 cursor_changed |= GetSelectionModel() != selection_model;
1391 if (cursor_changed)
1392 UpdateSelectionClipboard();
1393 UpdateAfterChange(text_changed, cursor_changed);
1394 OnAfterUserAction();
1397 ////////////////////////////////////////////////////////////////////////////////
1398 // Textfield, ui::TextInputClient overrides:
1400 void Textfield::SetCompositionText(const ui::CompositionText& composition) {
1401 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1402 return;
1404 OnBeforeUserAction();
1405 skip_input_method_cancel_composition_ = true;
1406 model_->SetCompositionText(composition);
1407 skip_input_method_cancel_composition_ = false;
1408 UpdateAfterChange(true, true);
1409 OnAfterUserAction();
1412 void Textfield::ConfirmCompositionText() {
1413 if (!model_->HasCompositionText())
1414 return;
1416 OnBeforeUserAction();
1417 skip_input_method_cancel_composition_ = true;
1418 model_->ConfirmCompositionText();
1419 skip_input_method_cancel_composition_ = false;
1420 UpdateAfterChange(true, true);
1421 OnAfterUserAction();
1424 void Textfield::ClearCompositionText() {
1425 if (!model_->HasCompositionText())
1426 return;
1428 OnBeforeUserAction();
1429 skip_input_method_cancel_composition_ = true;
1430 model_->CancelCompositionText();
1431 skip_input_method_cancel_composition_ = false;
1432 UpdateAfterChange(true, true);
1433 OnAfterUserAction();
1436 void Textfield::InsertText(const base::string16& new_text) {
1437 // TODO(suzhe): Filter invalid characters.
1438 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || new_text.empty())
1439 return;
1441 OnBeforeUserAction();
1442 skip_input_method_cancel_composition_ = true;
1443 if (GetRenderText()->insert_mode())
1444 model_->InsertText(new_text);
1445 else
1446 model_->ReplaceText(new_text);
1447 skip_input_method_cancel_composition_ = false;
1448 UpdateAfterChange(true, true);
1449 OnAfterUserAction();
1452 void Textfield::InsertChar(base::char16 ch, int flags) {
1453 // Filter out all control characters, including tab and new line characters,
1454 // and all characters with Alt modifier (and Search on ChromeOS). But allow
1455 // characters with the AltGr modifier. On Windows AltGr is represented by
1456 // Alt+Ctrl, and on Linux it's a different flag that we don't care about.
1457 const bool should_insert_char =
1458 ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1459 !ui::IsSystemKeyModifier(flags);
1460 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || !should_insert_char)
1461 return;
1463 DoInsertChar(ch);
1465 if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD &&
1466 password_reveal_duration_ != base::TimeDelta()) {
1467 const size_t change_offset = model_->GetCursorPosition();
1468 DCHECK_GT(change_offset, 0u);
1469 RevealPasswordChar(change_offset - 1);
1473 ui::TextInputType Textfield::GetTextInputType() const {
1474 if (read_only() || !enabled())
1475 return ui::TEXT_INPUT_TYPE_NONE;
1476 return text_input_type_;
1479 ui::TextInputMode Textfield::GetTextInputMode() const {
1480 return ui::TEXT_INPUT_MODE_DEFAULT;
1483 int Textfield::GetTextInputFlags() const {
1484 return text_input_flags_;
1487 bool Textfield::CanComposeInline() const {
1488 return true;
1491 gfx::Rect Textfield::GetCaretBounds() const {
1492 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds();
1493 ConvertRectToScreen(this, &rect);
1494 return rect;
1497 bool Textfield::GetCompositionCharacterBounds(uint32 index,
1498 gfx::Rect* rect) const {
1499 DCHECK(rect);
1500 if (!HasCompositionText())
1501 return false;
1502 gfx::Range composition_range;
1503 model_->GetCompositionTextRange(&composition_range);
1504 DCHECK(!composition_range.is_empty());
1506 size_t text_index = composition_range.start() + index;
1507 if (composition_range.end() <= text_index)
1508 return false;
1509 gfx::RenderText* render_text = GetRenderText();
1510 if (!render_text->IsValidCursorIndex(text_index)) {
1511 text_index = render_text->IndexOfAdjacentGrapheme(
1512 text_index, gfx::CURSOR_BACKWARD);
1514 if (text_index < composition_range.start())
1515 return false;
1516 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD);
1517 *rect = render_text->GetCursorBounds(caret, false);
1518 ConvertRectToScreen(this, rect);
1519 return true;
1522 bool Textfield::HasCompositionText() const {
1523 return model_->HasCompositionText();
1526 bool Textfield::GetTextRange(gfx::Range* range) const {
1527 if (!ImeEditingAllowed())
1528 return false;
1530 model_->GetTextRange(range);
1531 return true;
1534 bool Textfield::GetCompositionTextRange(gfx::Range* range) const {
1535 if (!ImeEditingAllowed())
1536 return false;
1538 model_->GetCompositionTextRange(range);
1539 return true;
1542 bool Textfield::GetSelectionRange(gfx::Range* range) const {
1543 if (!ImeEditingAllowed())
1544 return false;
1545 *range = GetRenderText()->selection();
1546 return true;
1549 bool Textfield::SetSelectionRange(const gfx::Range& range) {
1550 if (!ImeEditingAllowed() || !range.IsValid())
1551 return false;
1552 OnBeforeUserAction();
1553 SelectRange(range);
1554 OnAfterUserAction();
1555 return true;
1558 bool Textfield::DeleteRange(const gfx::Range& range) {
1559 if (!ImeEditingAllowed() || range.is_empty())
1560 return false;
1562 OnBeforeUserAction();
1563 model_->SelectRange(range);
1564 if (model_->HasSelection()) {
1565 model_->DeleteSelection();
1566 UpdateAfterChange(true, true);
1568 OnAfterUserAction();
1569 return true;
1572 bool Textfield::GetTextFromRange(const gfx::Range& range,
1573 base::string16* range_text) const {
1574 if (!ImeEditingAllowed() || !range.IsValid())
1575 return false;
1577 gfx::Range text_range;
1578 if (!GetTextRange(&text_range) || !text_range.Contains(range))
1579 return false;
1581 *range_text = model_->GetTextFromRange(range);
1582 return true;
1585 void Textfield::OnInputMethodChanged() {}
1587 bool Textfield::ChangeTextDirectionAndLayoutAlignment(
1588 base::i18n::TextDirection direction) {
1589 // Restore text directionality mode when the indicated direction matches the
1590 // current forced mode; otherwise, force the mode indicated. This helps users
1591 // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1592 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ?
1593 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
1594 if (mode == GetRenderText()->directionality_mode())
1595 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
1596 else
1597 GetRenderText()->SetDirectionalityMode(mode);
1598 SchedulePaint();
1599 return true;
1602 void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) {
1603 gfx::Range range = GetRenderText()->selection();
1604 DCHECK_GE(range.start(), before);
1606 range.set_start(range.start() - before);
1607 range.set_end(range.end() + after);
1608 gfx::Range text_range;
1609 if (GetTextRange(&text_range) && text_range.Contains(range))
1610 DeleteRange(range);
1613 void Textfield::EnsureCaretInRect(const gfx::Rect& rect) {}
1615 bool Textfield::IsEditCommandEnabled(int command_id) {
1616 return IsCommandIdEnabled(command_id);
1619 void Textfield::SetEditCommandForNextKeyEvent(int command_id) {
1620 DCHECK_EQ(kNoCommand, scheduled_edit_command_);
1621 scheduled_edit_command_ = command_id;
1624 ////////////////////////////////////////////////////////////////////////////////
1625 // Textfield, protected:
1627 void Textfield::DoInsertChar(base::char16 ch) {
1628 OnBeforeUserAction();
1629 skip_input_method_cancel_composition_ = true;
1630 if (GetRenderText()->insert_mode())
1631 model_->InsertChar(ch);
1632 else
1633 model_->ReplaceChar(ch);
1634 skip_input_method_cancel_composition_ = false;
1636 UpdateAfterChange(true, true);
1637 OnAfterUserAction();
1640 gfx::RenderText* Textfield::GetRenderText() const {
1641 return model_->render_text();
1644 base::string16 Textfield::GetSelectionClipboardText() const {
1645 base::string16 selection_clipboard_text;
1646 ui::Clipboard::GetForCurrentThread()->ReadText(
1647 ui::CLIPBOARD_TYPE_SELECTION, &selection_clipboard_text);
1648 return selection_clipboard_text;
1651 ////////////////////////////////////////////////////////////////////////////////
1652 // Textfield, private:
1654 void Textfield::AccessibilitySetValue(const base::string16& new_value) {
1655 if (!read_only()) {
1656 SetText(new_value);
1657 ClearSelection();
1661 void Textfield::UpdateBackgroundColor() {
1662 const SkColor color = GetBackgroundColor();
1663 set_background(Background::CreateSolidBackground(color));
1664 // Disable subpixel rendering when the background color is transparent
1665 // because it draws incorrect colors around the glyphs in that case.
1666 // See crbug.com/115198
1667 GetRenderText()->set_subpixel_rendering_suppressed(
1668 SkColorGetA(color) != 0xFF);
1669 SchedulePaint();
1672 void Textfield::UpdateAfterChange(bool text_changed, bool cursor_changed) {
1673 if (text_changed) {
1674 if (controller_)
1675 controller_->ContentsChanged(this, text());
1676 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
1678 if (cursor_changed) {
1679 cursor_visible_ = true;
1680 RepaintCursor();
1681 if (cursor_repaint_timer_.IsRunning())
1682 cursor_repaint_timer_.Reset();
1683 if (!text_changed) {
1684 // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire
1685 // this if only the selection changed.
1686 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED, true);
1689 if (text_changed || cursor_changed) {
1690 OnCaretBoundsChanged();
1691 SchedulePaint();
1695 void Textfield::UpdateCursor() {
1696 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1697 cursor_visible_ = !cursor_visible_ || (caret_blink_ms == 0);
1698 RepaintCursor();
1701 void Textfield::RepaintCursor() {
1702 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds());
1703 r.Inset(-1, -1, -1, -1);
1704 SchedulePaintInRect(r);
1707 void Textfield::PaintTextAndCursor(gfx::Canvas* canvas) {
1708 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
1709 canvas->Save();
1711 // Draw placeholder text if needed.
1712 gfx::RenderText* render_text = GetRenderText();
1713 if (text().empty() && !GetPlaceholderText().empty()) {
1714 canvas->DrawStringRect(GetPlaceholderText(), GetFontList(),
1715 placeholder_text_color(), render_text->display_rect());
1718 // Draw the text, cursor, and selection.
1719 render_text->set_cursor_visible(cursor_visible_ && !drop_cursor_visible_ &&
1720 !HasSelection());
1721 render_text->Draw(canvas);
1723 // Draw the detached drop cursor that marks where the text will be dropped.
1724 if (drop_cursor_visible_)
1725 render_text->DrawCursor(canvas, drop_cursor_position_);
1727 canvas->Restore();
1730 void Textfield::MoveCursorTo(const gfx::Point& point, bool select) {
1731 if (model_->MoveCursorTo(point, select))
1732 UpdateAfterChange(false, true);
1735 void Textfield::SelectThroughLastDragLocation() {
1736 OnBeforeUserAction();
1737 model_->MoveCursorTo(last_drag_location_, true);
1738 if (aggregated_clicks_ == 1) {
1739 model_->SelectWord();
1740 // Expand the selection so the initially selected word remains selected.
1741 gfx::Range selection = GetRenderText()->selection();
1742 const size_t min = std::min(selection.GetMin(),
1743 double_click_word_.GetMin());
1744 const size_t max = std::max(selection.GetMax(),
1745 double_click_word_.GetMax());
1746 const bool reversed = selection.is_reversed();
1747 selection.set_start(reversed ? max : min);
1748 selection.set_end(reversed ? min : max);
1749 model_->SelectRange(selection);
1751 UpdateAfterChange(false, true);
1752 OnAfterUserAction();
1755 void Textfield::OnCaretBoundsChanged() {
1756 if (GetInputMethod())
1757 GetInputMethod()->OnCaretBoundsChanged(this);
1758 if (touch_selection_controller_)
1759 touch_selection_controller_->SelectionChanged();
1762 void Textfield::OnBeforeUserAction() {
1763 DCHECK(!performing_user_action_);
1764 performing_user_action_ = true;
1765 if (controller_)
1766 controller_->OnBeforeUserAction(this);
1769 void Textfield::OnAfterUserAction() {
1770 if (controller_)
1771 controller_->OnAfterUserAction(this);
1772 DCHECK(performing_user_action_);
1773 performing_user_action_ = false;
1776 bool Textfield::Cut() {
1777 if (!read_only() && text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD &&
1778 model_->Cut()) {
1779 if (controller_)
1780 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1781 return true;
1783 return false;
1786 bool Textfield::Copy() {
1787 if (text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD && model_->Copy()) {
1788 if (controller_)
1789 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE);
1790 return true;
1792 return false;
1795 bool Textfield::Paste() {
1796 if (!read_only() && model_->Paste()) {
1797 if (controller_)
1798 controller_->OnAfterPaste();
1799 return true;
1801 return false;
1804 void Textfield::UpdateContextMenu() {
1805 if (!context_menu_contents_.get()) {
1806 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1807 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1808 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1809 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1810 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1811 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1812 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
1813 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1814 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1815 IDS_APP_SELECT_ALL);
1816 if (controller_)
1817 controller_->UpdateContextMenu(context_menu_contents_.get());
1819 context_menu_runner_.reset(
1820 new MenuRunner(context_menu_contents_.get(),
1821 MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU));
1824 void Textfield::TrackMouseClicks(const ui::MouseEvent& event) {
1825 if (event.IsOnlyLeftMouseButton()) {
1826 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
1827 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
1828 !ExceededDragThreshold(event.location() - last_click_location_)) {
1829 // Upon clicking after a triple click, the count should go back to double
1830 // click and alternate between double and triple. This assignment maps
1831 // 0 to 1, 1 to 2, 2 to 1.
1832 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
1833 } else {
1834 aggregated_clicks_ = 0;
1836 last_click_time_ = event.time_stamp();
1837 last_click_location_ = event.location();
1841 bool Textfield::ImeEditingAllowed() const {
1842 // Disallow input method editing of password fields.
1843 ui::TextInputType t = GetTextInputType();
1844 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
1847 void Textfield::RevealPasswordChar(int index) {
1848 GetRenderText()->SetObscuredRevealIndex(index);
1849 SchedulePaint();
1851 if (index != -1) {
1852 password_reveal_timer_.Start(FROM_HERE, password_reveal_duration_,
1853 base::Bind(&Textfield::RevealPasswordChar,
1854 weak_ptr_factory_.GetWeakPtr(), -1));
1858 void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
1859 if (!HasFocus())
1860 return;
1862 if (!touch_selection_controller_) {
1863 touch_selection_controller_.reset(
1864 ui::TouchEditingControllerDeprecated::Create(this));
1866 if (touch_selection_controller_)
1867 touch_selection_controller_->SelectionChanged();
1870 void Textfield::UpdateSelectionClipboard() const {
1871 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1872 if (performing_user_action_ && HasSelection()) {
1873 ui::ScopedClipboardWriter(
1874 ui::CLIPBOARD_TYPE_SELECTION).WriteText(GetSelectedText());
1875 if (controller_)
1876 controller_->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION);
1878 #endif
1881 void Textfield::PasteSelectionClipboard(const ui::MouseEvent& event) {
1882 DCHECK(event.IsOnlyMiddleMouseButton());
1883 DCHECK(!read_only());
1884 base::string16 selection_clipboard_text = GetSelectionClipboardText();
1885 OnBeforeUserAction();
1886 const gfx::SelectionModel mouse =
1887 GetRenderText()->FindCursorPosition(event.location());
1888 if (!HasFocus())
1889 RequestFocus();
1890 model_->MoveCursorTo(mouse);
1891 if (!selection_clipboard_text.empty()) {
1892 model_->InsertText(selection_clipboard_text);
1893 UpdateAfterChange(true, true);
1895 OnAfterUserAction();
1898 } // namespace views