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