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