1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/controls/textfield/native_textfield_views.h"
6
7 #include <algorithm>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/i18n/case_conversion.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "grit/ui_strings.h"
17 #include "third_party/icu/source/common/unicode/uchar.h"
18 #include "third_party/skia/include/core/SkColor.h"
19 #include "ui/base/clipboard/clipboard.h"
20 #include "ui/base/dragdrop/drag_drop_types.h"
21 #include "ui/base/dragdrop/drag_utils.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/ui_base_switches_util.h"
24 #include "ui/compositor/layer.h"
25 #include "ui/events/event.h"
26 #include "ui/gfx/canvas.h"
27 #include "ui/gfx/insets.h"
28 #include "ui/gfx/range/range.h"
29 #include "ui/gfx/render_text.h"
30 #include "ui/gfx/text_constants.h"
31 #include "ui/native_theme/native_theme.h"
32 #include "ui/views/background.h"
33 #include "ui/views/border.h"
34 #include "ui/views/controls/focusable_border.h"
35 #include "ui/views/controls/menu/menu_item_view.h"
36 #include "ui/views/controls/menu/menu_model_adapter.h"
37 #include "ui/views/controls/menu/menu_runner.h"
38 #include "ui/views/controls/textfield/textfield.h"
39 #include "ui/views/controls/textfield/textfield_controller.h"
40 #include "ui/views/controls/textfield/textfield_views_model.h"
41 #include "ui/views/drag_utils.h"
42 #include "ui/views/ime/input_method.h"
43 #include "ui/views/metrics.h"
44 #include "ui/views/widget/widget.h"
45
46 #if defined(USE_AURA)
47 #include "ui/base/cursor/cursor.h"
48 #endif
49
50 #if defined(OS_WIN) && defined(USE_AURA)
51 #include "base/win/win_util.h"
52 #endif
53
54 namespace {
55
ConvertRectToScreen(const views::View * src,gfx::Rect * r)56 void ConvertRectToScreen(const views::View* src, gfx::Rect* r) {
57 DCHECK(src);
58
59 gfx::Point new_origin = r->origin();
60 views::View::ConvertPointToScreen(src, &new_origin);
61 r->set_origin(new_origin);
62 }
63
64 } // namespace
65
66 namespace views {
67
68 const char NativeTextfieldViews::kViewClassName[] =
69 "views/NativeTextfieldViews";
70
NativeTextfieldViews(Textfield * parent)71 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
72 : textfield_(parent),
73 model_(new TextfieldViewsModel(this)),
74 text_border_(new FocusableBorder()),
75 is_cursor_visible_(false),
76 is_drop_cursor_visible_(false),
77 skip_input_method_cancel_composition_(false),
78 initiating_drag_(false),
79 cursor_timer_(this),
80 aggregated_clicks_(0) {
81 set_border(text_border_);
82 GetRenderText()->SetFontList(textfield_->font_list());
83 UpdateColorsFromTheme(GetNativeTheme());
84 set_context_menu_controller(this);
85 parent->set_context_menu_controller(this);
86 set_drag_controller(this);
87 }
88
~NativeTextfieldViews()89 NativeTextfieldViews::~NativeTextfieldViews() {
90 }
91
92 ////////////////////////////////////////////////////////////////////////////////
93 // NativeTextfieldViews, View overrides:
94
OnMousePressed(const ui::MouseEvent & event)95 bool NativeTextfieldViews::OnMousePressed(const ui::MouseEvent& event) {
96 OnBeforeUserAction();
97 TrackMouseClicks(event);
98
99 TextfieldController* controller = textfield_->GetController();
100 if (!(controller && controller->HandleMouseEvent(textfield_, event)) &&
101 !textfield_->OnMousePressed(event)) {
102 HandleMousePressEvent(event);
103 }
104
105 OnAfterUserAction();
106 touch_selection_controller_.reset();
107 return true;
108 }
109
ExceededDragThresholdFromLastClickLocation(const ui::MouseEvent & event)110 bool NativeTextfieldViews::ExceededDragThresholdFromLastClickLocation(
111 const ui::MouseEvent& event) {
112 return ExceededDragThreshold(event.location() - last_click_location_);
113 }
114
OnMouseDragged(const ui::MouseEvent & event)115 bool NativeTextfieldViews::OnMouseDragged(const ui::MouseEvent& event) {
116 // Don't adjust the cursor on a potential drag and drop, or if the mouse
117 // movement from the last mouse click does not exceed the drag threshold.
118 if (initiating_drag_ || !ExceededDragThresholdFromLastClickLocation(event) ||
119 !event.IsOnlyLeftMouseButton()) {
120 return true;
121 }
122
123 OnBeforeUserAction();
124 // TODO: Remove once NativeTextfield implementations are consolidated to
125 // Textfield.
126 if (!textfield_->OnMouseDragged(event)) {
127 MoveCursorTo(event.location(), true);
128 if (aggregated_clicks_ == 1) {
129 model_->SelectWord();
130 // Expand the selection so the initially selected word remains selected.
131 gfx::Range selection = GetRenderText()->selection();
132 const size_t min = std::min(selection.GetMin(),
133 double_click_word_.GetMin());
134 const size_t max = std::max(selection.GetMax(),
135 double_click_word_.GetMax());
136 const bool reversed = selection.is_reversed();
137 selection.set_start(reversed ? max : min);
138 selection.set_end(reversed ? min : max);
139 model_->SelectRange(selection);
140 }
141 SchedulePaint();
142 }
143 OnAfterUserAction();
144 return true;
145 }
146
OnMouseReleased(const ui::MouseEvent & event)147 void NativeTextfieldViews::OnMouseReleased(const ui::MouseEvent& event) {
148 OnBeforeUserAction();
149 // TODO: Remove once NativeTextfield implementations are consolidated to
150 // Textfield.
151 textfield_->OnMouseReleased(event);
152 // Cancel suspected drag initiations, the user was clicking in the selection.
153 if (initiating_drag_ && MoveCursorTo(event.location(), false))
154 SchedulePaint();
155 initiating_drag_ = false;
156 OnAfterUserAction();
157 }
158
OnGestureEvent(ui::GestureEvent * event)159 void NativeTextfieldViews::OnGestureEvent(ui::GestureEvent* event) {
160 textfield_->OnGestureEvent(event);
161 if (event->handled())
162 return;
163
164 switch (event->type()) {
165 case ui::ET_GESTURE_TAP_DOWN:
166 OnBeforeUserAction();
167 textfield_->RequestFocus();
168 // We don't deselect if the point is in the selection
169 // because TAP_DOWN may turn into a LONG_PRESS.
170 if (!GetRenderText()->IsPointInSelection(event->location()) &&
171 MoveCursorTo(event->location(), false))
172 SchedulePaint();
173 OnAfterUserAction();
174 event->SetHandled();
175 break;
176 case ui::ET_GESTURE_SCROLL_UPDATE:
177 OnBeforeUserAction();
178 if (MoveCursorTo(event->location(), true))
179 SchedulePaint();
180 OnAfterUserAction();
181 event->SetHandled();
182 break;
183 case ui::ET_GESTURE_SCROLL_END:
184 case ui::ET_SCROLL_FLING_START:
185 CreateTouchSelectionControllerAndNotifyIt();
186 event->SetHandled();
187 break;
188 case ui::ET_GESTURE_TAP:
189 if (event->details().tap_count() == 1) {
190 CreateTouchSelectionControllerAndNotifyIt();
191 } else {
192 OnBeforeUserAction();
193 SelectAll(false);
194 OnAfterUserAction();
195 event->SetHandled();
196 }
197 break;
198 case ui::ET_GESTURE_LONG_PRESS:
199 // If long press happens outside selection, select word and show context
200 // menu (If touch selection is enabled, context menu is shown by the
201 // |touch_selection_controller_|, hence we mark the event handled.
202 // Otherwise, the regular context menu will be shown by views).
203 // If long press happens in selected text and touch drag drop is enabled,
204 // we will turn off touch selection (if one exists) and let views do drag
205 // drop.
206 if (!GetRenderText()->IsPointInSelection(event->location())) {
207 OnBeforeUserAction();
208 model_->SelectWord();
209 touch_selection_controller_.reset(
210 ui::TouchSelectionController::create(this));
211 OnCaretBoundsChanged();
212 SchedulePaint();
213 OnAfterUserAction();
214 if (touch_selection_controller_.get())
215 event->SetHandled();
216 } else if (switches::IsTouchDragDropEnabled()) {
217 initiating_drag_ = true;
218 touch_selection_controller_.reset();
219 } else {
220 if (!touch_selection_controller_.get())
221 CreateTouchSelectionControllerAndNotifyIt();
222 if (touch_selection_controller_.get())
223 event->SetHandled();
224 }
225 return;
226 case ui::ET_GESTURE_LONG_TAP:
227 if (!touch_selection_controller_.get())
228 CreateTouchSelectionControllerAndNotifyIt();
229
230 // If touch selection is enabled, the context menu on long tap will be
231 // shown by the |touch_selection_controller_|, hence we mark the event
232 // handled so views does not try to show context menu on it.
233 if (touch_selection_controller_.get())
234 event->SetHandled();
235 break;
236 default:
237 View::OnGestureEvent(event);
238 return;
239 }
240 PlatformGestureEventHandling(event);
241 }
242
OnKeyPressed(const ui::KeyEvent & event)243 bool NativeTextfieldViews::OnKeyPressed(const ui::KeyEvent& event) {
244 // OnKeyPressed/OnKeyReleased/OnFocus/OnBlur will never be invoked on
245 // NativeTextfieldViews as it will never gain focus.
246 NOTREACHED();
247 return false;
248 }
249
OnKeyReleased(const ui::KeyEvent & event)250 bool NativeTextfieldViews::OnKeyReleased(const ui::KeyEvent& event) {
251 NOTREACHED();
252 return false;
253 }
254
GetDropFormats(int * formats,std::set<OSExchangeData::CustomFormat> * custom_formats)255 bool NativeTextfieldViews::GetDropFormats(
256 int* formats,
257 std::set<OSExchangeData::CustomFormat>* custom_formats) {
258 if (!textfield_->enabled() || textfield_->read_only())
259 return false;
260 // TODO(msw): Can we support URL, FILENAME, etc.?
261 *formats = ui::OSExchangeData::STRING;
262 TextfieldController* controller = textfield_->GetController();
263 if (controller)
264 controller->AppendDropFormats(formats, custom_formats);
265 return true;
266 }
267
CanDrop(const OSExchangeData & data)268 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) {
269 int formats;
270 std::set<OSExchangeData::CustomFormat> custom_formats;
271 GetDropFormats(&formats, &custom_formats);
272 return textfield_->enabled() && !textfield_->read_only() &&
273 data.HasAnyFormat(formats, custom_formats);
274 }
275
OnDragUpdated(const ui::DropTargetEvent & event)276 int NativeTextfieldViews::OnDragUpdated(const ui::DropTargetEvent& event) {
277 DCHECK(CanDrop(event.data()));
278
279 const gfx::Range& selection = GetRenderText()->selection();
280 drop_cursor_position_ = GetRenderText()->FindCursorPosition(event.location());
281 bool in_selection = !selection.is_empty() &&
282 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos()));
283 is_drop_cursor_visible_ = !in_selection;
284 // TODO(msw): Pan over text when the user drags to the visible text edge.
285 OnCaretBoundsChanged();
286 SchedulePaint();
287
288 if (initiating_drag_) {
289 if (in_selection)
290 return ui::DragDropTypes::DRAG_NONE;
291 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
292 ui::DragDropTypes::DRAG_MOVE;
293 }
294 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
295 }
296
OnDragExited()297 void NativeTextfieldViews::OnDragExited() {
298 is_drop_cursor_visible_ = false;
299 SchedulePaint();
300 }
301
OnPerformDrop(const ui::DropTargetEvent & event)302 int NativeTextfieldViews::OnPerformDrop(const ui::DropTargetEvent& event) {
303 DCHECK(CanDrop(event.data()));
304
305 is_drop_cursor_visible_ = false;
306
307 TextfieldController* controller = textfield_->GetController();
308 if (controller) {
309 int drag_operation = controller->OnDrop(event.data());
310 if (drag_operation != ui::DragDropTypes::DRAG_NONE)
311 return drag_operation;
312 }
313
314 DCHECK(!initiating_drag_ ||
315 !GetRenderText()->IsPointInSelection(event.location()));
316 OnBeforeUserAction();
317 skip_input_method_cancel_composition_ = true;
318
319 gfx::SelectionModel drop_destination_model =
320 GetRenderText()->FindCursorPosition(event.location());
321 string16 text;
322 event.data().GetString(&text);
323 text = GetTextForDisplay(text);
324
325 // Delete the current selection for a drag and drop within this view.
326 const bool move = initiating_drag_ && !event.IsControlDown() &&
327 event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
328 if (move) {
329 // Adjust the drop destination if it is on or after the current selection.
330 size_t drop = drop_destination_model.caret_pos();
331 drop -= GetSelectedRange().Intersect(gfx::Range(0, drop)).length();
332 model_->DeleteSelectionAndInsertTextAt(text, drop);
333 } else {
334 model_->MoveCursorTo(drop_destination_model);
335 // Drop always inserts text even if the textfield is not in insert mode.
336 model_->InsertText(text);
337 }
338 skip_input_method_cancel_composition_ = false;
339 UpdateAfterChange(true, true);
340 OnAfterUserAction();
341 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
342 }
343
OnDragDone()344 void NativeTextfieldViews::OnDragDone() {
345 initiating_drag_ = false;
346 is_drop_cursor_visible_ = false;
347 }
348
OnPaint(gfx::Canvas * canvas)349 void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) {
350 OnPaintBackground(canvas);
351 PaintTextAndCursor(canvas);
352 if (textfield_->draw_border())
353 OnPaintBorder(canvas);
354 }
355
OnFocus()356 void NativeTextfieldViews::OnFocus() {
357 NOTREACHED();
358 }
359
OnBlur()360 void NativeTextfieldViews::OnBlur() {
361 NOTREACHED();
362 }
363
OnNativeThemeChanged(const ui::NativeTheme * theme)364 void NativeTextfieldViews::OnNativeThemeChanged(const ui::NativeTheme* theme) {
365 UpdateColorsFromTheme(theme);
366 }
367
SelectRect(const gfx::Point & start,const gfx::Point & end)368 void NativeTextfieldViews::SelectRect(const gfx::Point& start,
369 const gfx::Point& end) {
370 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
371 return;
372
373 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start);
374 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end);
375 gfx::SelectionModel selection(
376 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()),
377 end_caret.caret_affinity());
378
379 OnBeforeUserAction();
380 model_->SelectSelectionModel(selection);
381 OnCaretBoundsChanged();
382 SchedulePaint();
383 OnAfterUserAction();
384 }
385
MoveCaretTo(const gfx::Point & point)386 void NativeTextfieldViews::MoveCaretTo(const gfx::Point& point) {
387 SelectRect(point, point);
388 }
389
GetSelectionEndPoints(gfx::Rect * p1,gfx::Rect * p2)390 void NativeTextfieldViews::GetSelectionEndPoints(gfx::Rect* p1,
391 gfx::Rect* p2) {
392 gfx::RenderText* render_text = GetRenderText();
393 const gfx::SelectionModel& sel = render_text->selection_model();
394 gfx::SelectionModel start_sel =
395 render_text->GetSelectionModelForSelectionStart();
396 *p1 = render_text->GetCursorBounds(start_sel, true);
397 *p2 = render_text->GetCursorBounds(sel, true);
398 }
399
GetBounds()400 gfx::Rect NativeTextfieldViews::GetBounds() {
401 return bounds();
402 }
403
GetNativeView()404 gfx::NativeView NativeTextfieldViews::GetNativeView() {
405 return GetWidget()->GetNativeView();
406 }
407
ConvertPointToScreen(gfx::Point * point)408 void NativeTextfieldViews::ConvertPointToScreen(gfx::Point* point) {
409 View::ConvertPointToScreen(this, point);
410 }
411
ConvertPointFromScreen(gfx::Point * point)412 void NativeTextfieldViews::ConvertPointFromScreen(gfx::Point* point) {
413 View::ConvertPointFromScreen(this, point);
414 }
415
DrawsHandles()416 bool NativeTextfieldViews::DrawsHandles() {
417 return false;
418 }
419
OpenContextMenu(const gfx::Point & anchor)420 void NativeTextfieldViews::OpenContextMenu(const gfx::Point& anchor) {
421 touch_selection_controller_.reset();
422 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU);
423 }
424
GetCursor(const ui::MouseEvent & event)425 gfx::NativeCursor NativeTextfieldViews::GetCursor(const ui::MouseEvent& event) {
426 bool in_selection = GetRenderText()->IsPointInSelection(event.location());
427 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
428 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
429 #if defined(USE_AURA)
430 return text_cursor ? ui::kCursorIBeam : ui::kCursorNull;
431 #elif defined(OS_WIN)
432 static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM);
433 static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW);
434 return text_cursor ? ibeam : arrow;
435 #endif
436 }
437
438 /////////////////////////////////////////////////////////////////
439 // NativeTextfieldViews, ContextMenuController overrides:
ShowContextMenuForView(View * source,const gfx::Point & point,ui::MenuSourceType source_type)440 void NativeTextfieldViews::ShowContextMenuForView(
441 View* source,
442 const gfx::Point& point,
443 ui::MenuSourceType source_type) {
444 UpdateContextMenu();
445 if (context_menu_runner_->RunMenuAt(GetWidget(), NULL,
446 gfx::Rect(point, gfx::Size()), views::MenuItemView::TOPLEFT,
447 source_type,
448 MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
449 MenuRunner::MENU_DELETED)
450 return;
451 }
452
453 /////////////////////////////////////////////////////////////////
454 // NativeTextfieldViews, views::DragController overrides:
WriteDragDataForView(views::View * sender,const gfx::Point & press_pt,OSExchangeData * data)455 void NativeTextfieldViews::WriteDragDataForView(views::View* sender,
456 const gfx::Point& press_pt,
457 OSExchangeData* data) {
458 DCHECK_NE(ui::DragDropTypes::DRAG_NONE,
459 GetDragOperationsForView(sender, press_pt));
460 data->SetString(GetSelectedText());
461 scoped_ptr<gfx::Canvas> canvas(
462 views::GetCanvasForDragImage(textfield_->GetWidget(), size()));
463 GetRenderText()->DrawSelectedTextForDrag(canvas.get());
464 drag_utils::SetDragImageOnDataObject(*canvas, size(),
465 press_pt.OffsetFromOrigin(),
466 data);
467 TextfieldController* controller = textfield_->GetController();
468 if (controller)
469 controller->OnWriteDragData(data);
470 }
471
GetDragOperationsForView(views::View * sender,const gfx::Point & p)472 int NativeTextfieldViews::GetDragOperationsForView(views::View* sender,
473 const gfx::Point& p) {
474 int drag_operations = ui::DragDropTypes::DRAG_COPY;
475 if (!textfield_->enabled() || textfield_->IsObscured() ||
476 !GetRenderText()->IsPointInSelection(p))
477 drag_operations = ui::DragDropTypes::DRAG_NONE;
478 else if (sender == this && !textfield_->read_only())
479 drag_operations =
480 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
481 TextfieldController* controller = textfield_->GetController();
482 if (controller)
483 controller->OnGetDragOperationsForTextfield(&drag_operations);
484 return drag_operations;
485 }
486
CanStartDragForView(View * sender,const gfx::Point & press_pt,const gfx::Point & p)487 bool NativeTextfieldViews::CanStartDragForView(View* sender,
488 const gfx::Point& press_pt,
489 const gfx::Point& p) {
490 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt);
491 }
492
493 /////////////////////////////////////////////////////////////////
494 // NativeTextfieldViews, NativeTextifieldWrapper overrides:
495
GetText() const496 string16 NativeTextfieldViews::GetText() const {
497 return model_->GetText();
498 }
499
UpdateText()500 void NativeTextfieldViews::UpdateText() {
501 model_->SetText(GetTextForDisplay(textfield_->text()));
502 OnCaretBoundsChanged();
503 SchedulePaint();
504 textfield_->NotifyAccessibilityEvent(
505 ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
506 }
507
AppendText(const string16 & text)508 void NativeTextfieldViews::AppendText(const string16& text) {
509 if (text.empty())
510 return;
511 model_->Append(GetTextForDisplay(text));
512 OnCaretBoundsChanged();
513 SchedulePaint();
514 }
515
InsertOrReplaceText(const string16 & text)516 void NativeTextfieldViews::InsertOrReplaceText(const string16& text) {
517 if (text.empty())
518 return;
519 model_->InsertText(text);
520 OnCaretBoundsChanged();
521 SchedulePaint();
522 }
523
GetTextDirection() const524 base::i18n::TextDirection NativeTextfieldViews::GetTextDirection() const {
525 return GetRenderText()->GetTextDirection();
526 }
527
GetSelectedText() const528 string16 NativeTextfieldViews::GetSelectedText() const {
529 return model_->GetSelectedText();
530 }
531
SelectAll(bool reversed)532 void NativeTextfieldViews::SelectAll(bool reversed) {
533 model_->SelectAll(reversed);
534 OnCaretBoundsChanged();
535 SchedulePaint();
536 }
537
ClearSelection()538 void NativeTextfieldViews::ClearSelection() {
539 model_->ClearSelection();
540 OnCaretBoundsChanged();
541 SchedulePaint();
542 }
543
UpdateBorder()544 void NativeTextfieldViews::UpdateBorder() {
545 // By default, if a caller calls Textfield::RemoveBorder() and does not set
546 // any explicit margins, they should get zero margins. But also call
547 // UpdateXXXMargins() so we respect any explicitly-set margins.
548 //
549 // NOTE: If someday Textfield supports toggling |draw_border_| back on, we'll
550 // need to update this conditional to set the insets to their default values.
551 if (!textfield_->draw_border())
552 text_border_->SetInsets(0, 0, 0, 0);
553 UpdateHorizontalMargins();
554 UpdateVerticalMargins();
555 }
556
UpdateTextColor()557 void NativeTextfieldViews::UpdateTextColor() {
558 SetColor(textfield_->GetTextColor());
559 }
560
UpdateBackgroundColor()561 void NativeTextfieldViews::UpdateBackgroundColor() {
562 const SkColor color = textfield_->GetBackgroundColor();
563 set_background(Background::CreateSolidBackground(color));
564 GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF);
565 SchedulePaint();
566 }
567
UpdateReadOnly()568 void NativeTextfieldViews::UpdateReadOnly() {
569 OnTextInputTypeChanged();
570 }
571
UpdateFont()572 void NativeTextfieldViews::UpdateFont() {
573 GetRenderText()->SetFontList(textfield_->font_list());
574 OnCaretBoundsChanged();
575 }
576
UpdateIsObscured()577 void NativeTextfieldViews::UpdateIsObscured() {
578 GetRenderText()->SetObscured(textfield_->IsObscured());
579 OnCaretBoundsChanged();
580 SchedulePaint();
581 OnTextInputTypeChanged();
582 }
583
UpdateEnabled()584 void NativeTextfieldViews::UpdateEnabled() {
585 SetEnabled(textfield_->enabled());
586 SchedulePaint();
587 OnTextInputTypeChanged();
588 }
589
CalculateInsets()590 gfx::Insets NativeTextfieldViews::CalculateInsets() {
591 return GetInsets();
592 }
593
UpdateHorizontalMargins()594 void NativeTextfieldViews::UpdateHorizontalMargins() {
595 int left, right;
596 if (!textfield_->GetHorizontalMargins(&left, &right))
597 return;
598 gfx::Insets inset = GetInsets();
599 text_border_->SetInsets(inset.top(), left, inset.bottom(), right);
600 OnBoundsChanged(GetBounds());
601 }
602
UpdateVerticalMargins()603 void NativeTextfieldViews::UpdateVerticalMargins() {
604 int top, bottom;
605 if (!textfield_->GetVerticalMargins(&top, &bottom))
606 return;
607 gfx::Insets inset = GetInsets();
608 text_border_->SetInsets(top, inset.left(), bottom, inset.right());
609 OnBoundsChanged(GetBounds());
610 }
611
SetFocus()612 bool NativeTextfieldViews::SetFocus() {
613 return false;
614 }
615
GetView()616 View* NativeTextfieldViews::GetView() {
617 return this;
618 }
619
GetTestingHandle() const620 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const {
621 NOTREACHED();
622 return NULL;
623 }
624
IsIMEComposing() const625 bool NativeTextfieldViews::IsIMEComposing() const {
626 return model_->HasCompositionText();
627 }
628
GetSelectedRange() const629 gfx::Range NativeTextfieldViews::GetSelectedRange() const {
630 return GetRenderText()->selection();
631 }
632
SelectRange(const gfx::Range & range)633 void NativeTextfieldViews::SelectRange(const gfx::Range& range) {
634 model_->SelectRange(range);
635 OnCaretBoundsChanged();
636 SchedulePaint();
637 textfield_->NotifyAccessibilityEvent(
638 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true);
639 }
640
GetSelectionModel() const641 gfx::SelectionModel NativeTextfieldViews::GetSelectionModel() const {
642 return GetRenderText()->selection_model();
643 }
644
SelectSelectionModel(const gfx::SelectionModel & sel)645 void NativeTextfieldViews::SelectSelectionModel(
646 const gfx::SelectionModel& sel) {
647 model_->SelectSelectionModel(sel);
648 OnCaretBoundsChanged();
649 SchedulePaint();
650 }
651
GetCursorPosition() const652 size_t NativeTextfieldViews::GetCursorPosition() const {
653 return model_->GetCursorPosition();
654 }
655
GetCursorEnabled() const656 bool NativeTextfieldViews::GetCursorEnabled() const {
657 return GetRenderText()->cursor_enabled();
658 }
659
SetCursorEnabled(bool enabled)660 void NativeTextfieldViews::SetCursorEnabled(bool enabled) {
661 GetRenderText()->SetCursorEnabled(enabled);
662 }
663
HandleKeyPressed(const ui::KeyEvent & e)664 bool NativeTextfieldViews::HandleKeyPressed(const ui::KeyEvent& e) {
665 TextfieldController* controller = textfield_->GetController();
666 bool handled = false;
667 if (controller)
668 handled = controller->HandleKeyEvent(textfield_, e);
669 touch_selection_controller_.reset();
670 return handled || HandleKeyEvent(e);
671 }
672
HandleKeyReleased(const ui::KeyEvent & e)673 bool NativeTextfieldViews::HandleKeyReleased(const ui::KeyEvent& e) {
674 return false; // crbug.com/127520
675 }
676
HandleFocus()677 void NativeTextfieldViews::HandleFocus() {
678 GetRenderText()->set_focused(true);
679 is_cursor_visible_ = true;
680 SchedulePaint();
681 GetInputMethod()->OnFocus();
682 OnCaretBoundsChanged();
683
684 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
685 if (caret_blink_ms != 0) {
686 base::MessageLoop::current()->PostDelayedTask(
687 FROM_HERE,
688 base::Bind(&NativeTextfieldViews::UpdateCursor,
689 cursor_timer_.GetWeakPtr()),
690 base::TimeDelta::FromMilliseconds(caret_blink_ms));
691 }
692 }
693
HandleBlur()694 void NativeTextfieldViews::HandleBlur() {
695 GetRenderText()->set_focused(false);
696 GetInputMethod()->OnBlur();
697 // Stop blinking cursor.
698 cursor_timer_.InvalidateWeakPtrs();
699 if (is_cursor_visible_) {
700 is_cursor_visible_ = false;
701 RepaintCursor();
702 }
703
704 touch_selection_controller_.reset();
705 }
706
GetTextInputClient()707 ui::TextInputClient* NativeTextfieldViews::GetTextInputClient() {
708 return textfield_->read_only() ? NULL : this;
709 }
710
ClearEditHistory()711 void NativeTextfieldViews::ClearEditHistory() {
712 model_->ClearEditHistory();
713 }
714
GetFontHeight()715 int NativeTextfieldViews::GetFontHeight() {
716 return GetRenderText()->font_list().GetHeight();
717 }
718
GetTextfieldBaseline() const719 int NativeTextfieldViews::GetTextfieldBaseline() const {
720 return GetRenderText()->GetBaseline();
721 }
722
GetWidthNeededForText() const723 int NativeTextfieldViews::GetWidthNeededForText() const {
724 return GetRenderText()->GetContentWidth() + GetInsets().width();
725 }
726
ExecuteTextCommand(int command_id)727 void NativeTextfieldViews::ExecuteTextCommand(int command_id) {
728 ExecuteCommand(command_id, 0);
729 }
730
HasTextBeingDragged()731 bool NativeTextfieldViews::HasTextBeingDragged() {
732 return initiating_drag_;
733 }
734
GetContextMenuLocation()735 gfx::Point NativeTextfieldViews::GetContextMenuLocation() {
736 return GetCaretBounds().bottom_right();
737 }
738
739 /////////////////////////////////////////////////////////////////
740 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides:
741
IsCommandIdChecked(int command_id) const742 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const {
743 return true;
744 }
745
IsCommandIdEnabled(int command_id) const746 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const {
747 TextfieldController* controller = textfield_->GetController();
748 if (controller && controller->HandlesCommand(command_id))
749 return controller->IsCommandIdEnabled(command_id);
750
751 bool editable = !textfield_->read_only();
752 string16 result;
753 switch (command_id) {
754 case IDS_APP_UNDO:
755 return editable && model_->CanUndo();
756 case IDS_APP_CUT:
757 return editable && model_->HasSelection() && !textfield_->IsObscured();
758 case IDS_APP_COPY:
759 return model_->HasSelection() && !textfield_->IsObscured();
760 case IDS_APP_PASTE:
761 ui::Clipboard::GetForCurrentThread()->ReadText(
762 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
763 return editable && !result.empty();
764 case IDS_APP_DELETE:
765 return editable && model_->HasSelection();
766 case IDS_APP_SELECT_ALL:
767 return !model_->GetText().empty();
768 default:
769 return controller->IsCommandIdEnabled(command_id);
770 }
771 }
772
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)773 bool NativeTextfieldViews::GetAcceleratorForCommandId(int command_id,
774 ui::Accelerator* accelerator) {
775 return false;
776 }
777
IsItemForCommandIdDynamic(int command_id) const778 bool NativeTextfieldViews::IsItemForCommandIdDynamic(int command_id) const {
779 const TextfieldController* controller = textfield_->GetController();
780 return controller && controller->IsItemForCommandIdDynamic(command_id);
781 }
782
GetLabelForCommandId(int command_id) const783 string16 NativeTextfieldViews::GetLabelForCommandId(int command_id) const {
784 const TextfieldController* controller = textfield_->GetController();
785 return controller ? controller->GetLabelForCommandId(command_id) : string16();
786 }
787
ExecuteCommand(int command_id,int event_flags)788 void NativeTextfieldViews::ExecuteCommand(int command_id, int event_flags) {
789 touch_selection_controller_.reset();
790 if (!IsCommandIdEnabled(command_id))
791 return;
792
793 TextfieldController* controller = textfield_->GetController();
794 if (controller && controller->HandlesCommand(command_id)) {
795 controller->ExecuteCommand(command_id, 0);
796 } else {
797 bool text_changed = false;
798 switch (command_id) {
799 case IDS_APP_UNDO:
800 OnBeforeUserAction();
801 text_changed = model_->Undo();
802 UpdateAfterChange(text_changed, text_changed);
803 OnAfterUserAction();
804 break;
805 case IDS_APP_CUT:
806 OnBeforeUserAction();
807 text_changed = Cut();
808 UpdateAfterChange(text_changed, text_changed);
809 OnAfterUserAction();
810 break;
811 case IDS_APP_COPY:
812 OnBeforeUserAction();
813 Copy();
814 OnAfterUserAction();
815 break;
816 case IDS_APP_PASTE:
817 OnBeforeUserAction();
818 text_changed = Paste();
819 UpdateAfterChange(text_changed, text_changed);
820 OnAfterUserAction();
821 break;
822 case IDS_APP_DELETE:
823 OnBeforeUserAction();
824 text_changed = model_->Delete();
825 UpdateAfterChange(text_changed, text_changed);
826 OnAfterUserAction();
827 break;
828 case IDS_APP_SELECT_ALL:
829 OnBeforeUserAction();
830 SelectAll(false);
831 UpdateAfterChange(false, true);
832 OnAfterUserAction();
833 break;
834 default:
835 controller->ExecuteCommand(command_id, 0);
836 break;
837 }
838 }
839 }
840
SetColor(SkColor value)841 void NativeTextfieldViews::SetColor(SkColor value) {
842 GetRenderText()->SetColor(value);
843 SchedulePaint();
844 }
845
ApplyColor(SkColor value,const gfx::Range & range)846 void NativeTextfieldViews::ApplyColor(SkColor value, const gfx::Range& range) {
847 GetRenderText()->ApplyColor(value, range);
848 SchedulePaint();
849 }
850
SetStyle(gfx::TextStyle style,bool value)851 void NativeTextfieldViews::SetStyle(gfx::TextStyle style, bool value) {
852 GetRenderText()->SetStyle(style, value);
853 SchedulePaint();
854 }
855
ApplyStyle(gfx::TextStyle style,bool value,const gfx::Range & range)856 void NativeTextfieldViews::ApplyStyle(gfx::TextStyle style,
857 bool value,
858 const gfx::Range& range) {
859 GetRenderText()->ApplyStyle(style, value, range);
860 SchedulePaint();
861 }
862
OnBoundsChanged(const gfx::Rect & previous_bounds)863 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) {
864 // Set the RenderText display area.
865 gfx::Insets insets = GetInsets();
866 gfx::Rect display_rect(insets.left(),
867 insets.top(),
868 width() - insets.width(),
869 height() - insets.height());
870 GetRenderText()->SetDisplayRect(display_rect);
871 OnCaretBoundsChanged();
872 }
873
874 ///////////////////////////////////////////////////////////////////////////////
875 // NativeTextfieldViews, ui::TextInputClient implementation, private:
876
SetCompositionText(const ui::CompositionText & composition)877 void NativeTextfieldViews::SetCompositionText(
878 const ui::CompositionText& composition) {
879 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
880 return;
881
882 OnBeforeUserAction();
883 skip_input_method_cancel_composition_ = true;
884 model_->SetCompositionText(composition);
885 skip_input_method_cancel_composition_ = false;
886 UpdateAfterChange(true, true);
887 OnAfterUserAction();
888 }
889
ConfirmCompositionText()890 void NativeTextfieldViews::ConfirmCompositionText() {
891 if (!model_->HasCompositionText())
892 return;
893
894 OnBeforeUserAction();
895 skip_input_method_cancel_composition_ = true;
896 model_->ConfirmCompositionText();
897 skip_input_method_cancel_composition_ = false;
898 UpdateAfterChange(true, true);
899 OnAfterUserAction();
900 }
901
ClearCompositionText()902 void NativeTextfieldViews::ClearCompositionText() {
903 if (!model_->HasCompositionText())
904 return;
905
906 OnBeforeUserAction();
907 skip_input_method_cancel_composition_ = true;
908 model_->CancelCompositionText();
909 skip_input_method_cancel_composition_ = false;
910 UpdateAfterChange(true, true);
911 OnAfterUserAction();
912 }
913
InsertText(const string16 & text)914 void NativeTextfieldViews::InsertText(const string16& text) {
915 // TODO(suzhe): Filter invalid characters.
916 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty())
917 return;
918
919 OnBeforeUserAction();
920 skip_input_method_cancel_composition_ = true;
921 if (GetRenderText()->insert_mode())
922 model_->InsertText(GetTextForDisplay(text));
923 else
924 model_->ReplaceText(GetTextForDisplay(text));
925 skip_input_method_cancel_composition_ = false;
926 UpdateAfterChange(true, true);
927 OnAfterUserAction();
928 }
929
InsertChar(char16 ch,int flags)930 void NativeTextfieldViews::InsertChar(char16 ch, int flags) {
931 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE ||
932 !ShouldInsertChar(ch, flags)) {
933 return;
934 }
935
936 OnBeforeUserAction();
937 skip_input_method_cancel_composition_ = true;
938 if (GetRenderText()->insert_mode())
939 model_->InsertChar(ch);
940 else
941 model_->ReplaceChar(ch);
942 skip_input_method_cancel_composition_ = false;
943
944 model_->SetText(GetTextForDisplay(GetText()));
945
946 UpdateAfterChange(true, true);
947 OnAfterUserAction();
948
949 if (textfield_->IsObscured()) {
950 const base::TimeDelta& reveal_duration =
951 textfield_->obscured_reveal_duration();
952 if (reveal_duration != base::TimeDelta()) {
953 const size_t change_offset = model_->GetCursorPosition();
954 DCHECK_GT(change_offset, 0u);
955 RevealObscuredChar(change_offset - 1, reveal_duration);
956 }
957 }
958 }
959
GetAttachedWindow() const960 gfx::NativeWindow NativeTextfieldViews::GetAttachedWindow() const {
961 // Imagine the following hierarchy.
962 // [NativeWidget A] - FocusManager
963 // [View]
964 // [NativeWidget B]
965 // [View]
966 // [View X]
967 // An important thing is that [NativeWidget A] owns Win32 input focus even
968 // when [View X] is logically focused by FocusManager. As a result, an Win32
969 // IME may want to interact with the native view of [NativeWidget A] rather
970 // than that of [NativeWidget B]. This is why we need to call
971 // GetTopLevelWidget() here.
972 return GetWidget()->GetTopLevelWidget()->GetNativeView();
973 }
974
GetTextInputType() const975 ui::TextInputType NativeTextfieldViews::GetTextInputType() const {
976 return textfield_->GetTextInputType();
977 }
978
GetTextInputMode() const979 ui::TextInputMode NativeTextfieldViews::GetTextInputMode() const {
980 return ui::TEXT_INPUT_MODE_DEFAULT;
981 }
982
CanComposeInline() const983 bool NativeTextfieldViews::CanComposeInline() const {
984 return true;
985 }
986
GetCaretBounds() const987 gfx::Rect NativeTextfieldViews::GetCaretBounds() const {
988 // TextInputClient::GetCaretBounds is expected to return a value in screen
989 // coordinates.
990 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds();
991 ConvertRectToScreen(this, &rect);
992 return rect;
993 }
994
GetCompositionCharacterBounds(uint32 index,gfx::Rect * rect) const995 bool NativeTextfieldViews::GetCompositionCharacterBounds(
996 uint32 index,
997 gfx::Rect* rect) const {
998 DCHECK(rect);
999 if (!HasCompositionText())
1000 return false;
1001 const gfx::Range& composition_range = GetRenderText()->GetCompositionRange();
1002 DCHECK(!composition_range.is_empty());
1003
1004 size_t text_index = composition_range.start() + index;
1005 if (composition_range.end() <= text_index)
1006 return false;
1007 if (!GetRenderText()->IsCursorablePosition(text_index)) {
1008 text_index = GetRenderText()->IndexOfAdjacentGrapheme(
1009 text_index, gfx::CURSOR_BACKWARD);
1010 }
1011 if (text_index < composition_range.start())
1012 return false;
1013 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD);
1014 *rect = GetRenderText()->GetCursorBounds(caret, false);
1015 ConvertRectToScreen(this, rect);
1016
1017 return true;
1018 }
1019
HasCompositionText() const1020 bool NativeTextfieldViews::HasCompositionText() const {
1021 return model_->HasCompositionText();
1022 }
1023
GetTextRange(gfx::Range * range) const1024 bool NativeTextfieldViews::GetTextRange(gfx::Range* range) const {
1025 if (!ImeEditingAllowed())
1026 return false;
1027
1028 model_->GetTextRange(range);
1029 return true;
1030 }
1031
GetCompositionTextRange(gfx::Range * range) const1032 bool NativeTextfieldViews::GetCompositionTextRange(gfx::Range* range) const {
1033 if (!ImeEditingAllowed())
1034 return false;
1035
1036 model_->GetCompositionTextRange(range);
1037 return true;
1038 }
1039
GetSelectionRange(gfx::Range * range) const1040 bool NativeTextfieldViews::GetSelectionRange(gfx::Range* range) const {
1041 if (!ImeEditingAllowed())
1042 return false;
1043 *range = GetSelectedRange();
1044 return true;
1045 }
1046
SetSelectionRange(const gfx::Range & range)1047 bool NativeTextfieldViews::SetSelectionRange(const gfx::Range& range) {
1048 if (!ImeEditingAllowed() || !range.IsValid())
1049 return false;
1050
1051 OnBeforeUserAction();
1052 SelectRange(range);
1053 OnAfterUserAction();
1054 return true;
1055 }
1056
DeleteRange(const gfx::Range & range)1057 bool NativeTextfieldViews::DeleteRange(const gfx::Range& range) {
1058 if (!ImeEditingAllowed() || range.is_empty())
1059 return false;
1060
1061 OnBeforeUserAction();
1062 model_->SelectRange(range);
1063 if (model_->HasSelection()) {
1064 model_->DeleteSelection();
1065 UpdateAfterChange(true, true);
1066 }
1067 OnAfterUserAction();
1068 return true;
1069 }
1070
GetTextFromRange(const gfx::Range & range,string16 * text) const1071 bool NativeTextfieldViews::GetTextFromRange(
1072 const gfx::Range& range,
1073 string16* text) const {
1074 if (!ImeEditingAllowed() || !range.IsValid())
1075 return false;
1076
1077 gfx::Range text_range;
1078 if (!GetTextRange(&text_range) || !text_range.Contains(range))
1079 return false;
1080
1081 *text = model_->GetTextFromRange(range);
1082 return true;
1083 }
1084
OnInputMethodChanged()1085 void NativeTextfieldViews::OnInputMethodChanged() {
1086 // TODO(msw): NOTIMPLEMENTED(); see http://crbug.com/140402
1087 }
1088
ChangeTextDirectionAndLayoutAlignment(base::i18n::TextDirection direction)1089 bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment(
1090 base::i18n::TextDirection direction) {
1091 // Restore text directionality mode when the indicated direction matches the
1092 // current forced mode; otherwise, force the mode indicated. This helps users
1093 // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1094 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ?
1095 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR;
1096 if (mode == GetRenderText()->directionality_mode())
1097 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
1098 else
1099 GetRenderText()->SetDirectionalityMode(mode);
1100 SchedulePaint();
1101 return true;
1102 }
1103
ExtendSelectionAndDelete(size_t before,size_t after)1104 void NativeTextfieldViews::ExtendSelectionAndDelete(
1105 size_t before,
1106 size_t after) {
1107 gfx::Range range = GetSelectedRange();
1108 DCHECK_GE(range.start(), before);
1109
1110 range.set_start(range.start() - before);
1111 range.set_end(range.end() + after);
1112 gfx::Range text_range;
1113 if (GetTextRange(&text_range) && text_range.Contains(range))
1114 DeleteRange(range);
1115 }
1116
EnsureCaretInRect(const gfx::Rect & rect)1117 void NativeTextfieldViews::EnsureCaretInRect(const gfx::Rect& rect) {
1118 }
1119
OnCandidateWindowShown()1120 void NativeTextfieldViews::OnCandidateWindowShown() {
1121 }
1122
OnCandidateWindowUpdated()1123 void NativeTextfieldViews::OnCandidateWindowUpdated() {
1124 }
1125
OnCandidateWindowHidden()1126 void NativeTextfieldViews::OnCandidateWindowHidden() {
1127 }
1128
OnCompositionTextConfirmedOrCleared()1129 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() {
1130 if (skip_input_method_cancel_composition_)
1131 return;
1132 DCHECK(textfield_->GetInputMethod());
1133 textfield_->GetInputMethod()->CancelComposition(textfield_);
1134 }
1135
GetRenderText() const1136 gfx::RenderText* NativeTextfieldViews::GetRenderText() const {
1137 return model_->render_text();
1138 }
1139
GetTextForDisplay(const string16 & text)1140 string16 NativeTextfieldViews::GetTextForDisplay(const string16& text) {
1141 return textfield_->style() & Textfield::STYLE_LOWERCASE ?
1142 base::i18n::ToLower(text) : text;
1143 }
1144
UpdateColorsFromTheme(const ui::NativeTheme * theme)1145 void NativeTextfieldViews::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
1146 UpdateTextColor();
1147 UpdateBackgroundColor();
1148 gfx::RenderText* render_text = GetRenderText();
1149 render_text->set_cursor_color(textfield_->GetTextColor());
1150 render_text->set_selection_color(theme->GetSystemColor(
1151 ui::NativeTheme::kColorId_TextfieldSelectionColor));
1152 render_text->set_selection_background_focused_color(theme->GetSystemColor(
1153 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused));
1154 }
1155
UpdateCursor()1156 void NativeTextfieldViews::UpdateCursor() {
1157 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs();
1158 is_cursor_visible_ = !is_cursor_visible_ || (caret_blink_ms == 0);
1159 RepaintCursor();
1160 if (caret_blink_ms != 0) {
1161 base::MessageLoop::current()->PostDelayedTask(
1162 FROM_HERE,
1163 base::Bind(&NativeTextfieldViews::UpdateCursor,
1164 cursor_timer_.GetWeakPtr()),
1165 base::TimeDelta::FromMilliseconds(caret_blink_ms));
1166 }
1167 }
1168
RepaintCursor()1169 void NativeTextfieldViews::RepaintCursor() {
1170 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds());
1171 r.Inset(-1, -1, -1, -1);
1172 SchedulePaintInRect(r);
1173 }
1174
PaintTextAndCursor(gfx::Canvas * canvas)1175 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) {
1176 TRACE_EVENT0("views", "NativeTextfieldViews::PaintTextAndCursor");
1177 canvas->Save();
1178 GetRenderText()->set_cursor_visible(!is_drop_cursor_visible_ &&
1179 is_cursor_visible_ && !model_->HasSelection());
1180 // Draw the text, cursor, and selection.
1181 GetRenderText()->Draw(canvas);
1182
1183 // Draw the detached drop cursor that marks where the text will be dropped.
1184 if (is_drop_cursor_visible_)
1185 GetRenderText()->DrawCursor(canvas, drop_cursor_position_);
1186
1187 // Draw placeholder text if needed.
1188 if (model_->GetText().empty() &&
1189 !textfield_->GetPlaceholderText().empty()) {
1190 canvas->DrawStringInt(
1191 textfield_->GetPlaceholderText(),
1192 GetRenderText()->GetPrimaryFont(),
1193 textfield_->placeholder_text_color(),
1194 GetRenderText()->display_rect());
1195 }
1196 canvas->Restore();
1197 }
1198
HandleKeyEvent(const ui::KeyEvent & key_event)1199 bool NativeTextfieldViews::HandleKeyEvent(const ui::KeyEvent& key_event) {
1200 // TODO(oshima): Refactor and consolidate with ExecuteCommand.
1201 if (key_event.type() == ui::ET_KEY_PRESSED) {
1202 ui::KeyboardCode key_code = key_event.key_code();
1203 if (key_code == ui::VKEY_TAB || key_event.IsUnicodeKeyCode())
1204 return false;
1205
1206 OnBeforeUserAction();
1207 const bool editable = !textfield_->read_only();
1208 const bool readable = !textfield_->IsObscured();
1209 const bool shift = key_event.IsShiftDown();
1210 const bool control = key_event.IsControlDown();
1211 const bool alt = key_event.IsAltDown() || key_event.IsAltGrDown();
1212 bool text_changed = false;
1213 bool cursor_changed = false;
1214 switch (key_code) {
1215 case ui::VKEY_Z:
1216 if (control && !shift && !alt && editable)
1217 cursor_changed = text_changed = model_->Undo();
1218 else if (control && shift && !alt && editable)
1219 cursor_changed = text_changed = model_->Redo();
1220 break;
1221 case ui::VKEY_Y:
1222 if (control && !alt && editable)
1223 cursor_changed = text_changed = model_->Redo();
1224 break;
1225 case ui::VKEY_A:
1226 if (control && !alt) {
1227 model_->SelectAll(false);
1228 cursor_changed = true;
1229 }
1230 break;
1231 case ui::VKEY_X:
1232 if (control && !alt && editable && readable)
1233 cursor_changed = text_changed = Cut();
1234 break;
1235 case ui::VKEY_C:
1236 if (control && !alt && readable)
1237 Copy();
1238 break;
1239 case ui::VKEY_V:
1240 if (control && !alt && editable)
1241 cursor_changed = text_changed = Paste();
1242 break;
1243 case ui::VKEY_RIGHT:
1244 case ui::VKEY_LEFT: {
1245 // We should ignore the alt-left/right keys because alt key doesn't make
1246 // any special effects for them and they can be shortcut keys such like
1247 // forward/back of the browser history.
1248 if (alt)
1249 break;
1250 const gfx::Range selection_range = GetSelectedRange();
1251 model_->MoveCursor(
1252 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK,
1253 (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT,
1254 shift);
1255 cursor_changed = GetSelectedRange() != selection_range;
1256 break;
1257 }
1258 case ui::VKEY_END:
1259 case ui::VKEY_HOME:
1260 if ((key_code == ui::VKEY_HOME) ==
1261 (GetRenderText()->GetTextDirection() == base::i18n::RIGHT_TO_LEFT))
1262 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift);
1263 else
1264 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift);
1265 cursor_changed = true;
1266 break;
1267 case ui::VKEY_BACK:
1268 case ui::VKEY_DELETE:
1269 if (!editable)
1270 break;
1271 if (!model_->HasSelection()) {
1272 gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ?
1273 gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
1274 if (shift && control) {
1275 // If both shift and control are pressed, then erase up to the
1276 // beginning/end of the buffer in ChromeOS. In windows, do nothing.
1277 #if defined(OS_WIN)
1278 break;
1279 #else
1280 model_->MoveCursor(gfx::LINE_BREAK, direction, true);
1281 #endif
1282 } else if (control) {
1283 // If only control is pressed, then erase the previous/next word.
1284 model_->MoveCursor(gfx::WORD_BREAK, direction, true);
1285 }
1286 }
1287 if (key_code == ui::VKEY_BACK)
1288 model_->Backspace();
1289 else if (shift && model_->HasSelection() && readable)
1290 Cut();
1291 else
1292 model_->Delete();
1293
1294 // Consume backspace and delete keys even if the edit did nothing. This
1295 // prevents potential unintended side-effects of further event handling.
1296 text_changed = true;
1297 break;
1298 case ui::VKEY_INSERT:
1299 if (control && !shift && readable)
1300 Copy();
1301 else if (shift && !control && editable)
1302 cursor_changed = text_changed = Paste();
1303 break;
1304 default:
1305 break;
1306 }
1307
1308 // We must have input method in order to support text input.
1309 DCHECK(textfield_->GetInputMethod());
1310
1311 UpdateAfterChange(text_changed, cursor_changed);
1312 OnAfterUserAction();
1313 return (text_changed || cursor_changed);
1314 }
1315 return false;
1316 }
1317
MoveCursorTo(const gfx::Point & point,bool select)1318 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) {
1319 if (!model_->MoveCursorTo(point, select))
1320 return false;
1321 OnCaretBoundsChanged();
1322 return true;
1323 }
1324
PropagateTextChange()1325 void NativeTextfieldViews::PropagateTextChange() {
1326 textfield_->SyncText();
1327 }
1328
UpdateAfterChange(bool text_changed,bool cursor_changed)1329 void NativeTextfieldViews::UpdateAfterChange(bool text_changed,
1330 bool cursor_changed) {
1331 if (text_changed) {
1332 PropagateTextChange();
1333 textfield_->NotifyAccessibilityEvent(
1334 ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
1335 }
1336 if (cursor_changed) {
1337 is_cursor_visible_ = true;
1338 RepaintCursor();
1339 if (!text_changed) {
1340 // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire
1341 // this if only the selection changed.
1342 textfield_->NotifyAccessibilityEvent(
1343 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true);
1344 }
1345 }
1346 if (text_changed || cursor_changed) {
1347 OnCaretBoundsChanged();
1348 SchedulePaint();
1349 }
1350 }
1351
UpdateContextMenu()1352 void NativeTextfieldViews::UpdateContextMenu() {
1353 if (!context_menu_contents_.get()) {
1354 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1355 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
1356 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1357 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1358 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1359 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
1360 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE);
1361 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
1362 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
1363 IDS_APP_SELECT_ALL);
1364 TextfieldController* controller = textfield_->GetController();
1365 if (controller)
1366 controller->UpdateContextMenu(context_menu_contents_.get());
1367
1368 context_menu_delegate_.reset(
1369 new views::MenuModelAdapter(context_menu_contents_.get()));
1370 context_menu_runner_.reset(
1371 new MenuRunner(new views::MenuItemView(context_menu_delegate_.get())));
1372 }
1373
1374 context_menu_delegate_->BuildMenu(context_menu_runner_->GetMenu());
1375 }
1376
OnTextInputTypeChanged()1377 void NativeTextfieldViews::OnTextInputTypeChanged() {
1378 // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320.
1379 if (textfield_->GetInputMethod())
1380 textfield_->GetInputMethod()->OnTextInputTypeChanged(textfield_);
1381 }
1382
OnCaretBoundsChanged()1383 void NativeTextfieldViews::OnCaretBoundsChanged() {
1384 // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320.
1385 if (textfield_->GetInputMethod())
1386 textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_);
1387
1388 // Notify selection controller
1389 if (touch_selection_controller_.get())
1390 touch_selection_controller_->SelectionChanged();
1391 }
1392
OnBeforeUserAction()1393 void NativeTextfieldViews::OnBeforeUserAction() {
1394 TextfieldController* controller = textfield_->GetController();
1395 if (controller)
1396 controller->OnBeforeUserAction(textfield_);
1397 }
1398
OnAfterUserAction()1399 void NativeTextfieldViews::OnAfterUserAction() {
1400 TextfieldController* controller = textfield_->GetController();
1401 if (controller)
1402 controller->OnAfterUserAction(textfield_);
1403 }
1404
Cut()1405 bool NativeTextfieldViews::Cut() {
1406 if (!textfield_->read_only() && !textfield_->IsObscured() && model_->Cut()) {
1407 TextfieldController* controller = textfield_->GetController();
1408 if (controller)
1409 controller->OnAfterCutOrCopy();
1410 return true;
1411 }
1412 return false;
1413 }
1414
Copy()1415 bool NativeTextfieldViews::Copy() {
1416 if (!textfield_->IsObscured() && model_->Copy()) {
1417 TextfieldController* controller = textfield_->GetController();
1418 if (controller)
1419 controller->OnAfterCutOrCopy();
1420 return true;
1421 }
1422 return false;
1423 }
1424
Paste()1425 bool NativeTextfieldViews::Paste() {
1426 if (textfield_->read_only())
1427 return false;
1428
1429 const string16 original_text = GetText();
1430 const bool success = model_->Paste();
1431
1432 if (success) {
1433 // As Paste is handled in model_->Paste(), the RenderText may contain
1434 // upper case characters. This is not consistent with other places
1435 // which keeps RenderText only containing lower case characters.
1436 string16 new_text = GetTextForDisplay(GetText());
1437 model_->SetText(new_text);
1438
1439 TextfieldController* controller = textfield_->GetController();
1440 if (controller)
1441 controller->OnAfterPaste();
1442 }
1443 return success;
1444 }
1445
TrackMouseClicks(const ui::MouseEvent & event)1446 void NativeTextfieldViews::TrackMouseClicks(const ui::MouseEvent& event) {
1447 if (event.IsOnlyLeftMouseButton()) {
1448 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
1449 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
1450 !ExceededDragThresholdFromLastClickLocation(event)) {
1451 // Upon clicking after a triple click, the count should go back to double
1452 // click and alternate between double and triple. This assignment maps
1453 // 0 to 1, 1 to 2, 2 to 1.
1454 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1;
1455 } else {
1456 aggregated_clicks_ = 0;
1457 }
1458 last_click_time_ = event.time_stamp();
1459 last_click_location_ = event.location();
1460 }
1461 }
1462
HandleMousePressEvent(const ui::MouseEvent & event)1463 void NativeTextfieldViews::HandleMousePressEvent(const ui::MouseEvent& event) {
1464 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton())
1465 textfield_->RequestFocus();
1466
1467 if (!event.IsOnlyLeftMouseButton())
1468 return;
1469
1470 initiating_drag_ = false;
1471 bool can_drag = true;
1472
1473 switch (aggregated_clicks_) {
1474 case 0:
1475 if (can_drag && GetRenderText()->IsPointInSelection(event.location()))
1476 initiating_drag_ = true;
1477 else
1478 MoveCursorTo(event.location(), event.IsShiftDown());
1479 break;
1480 case 1:
1481 MoveCursorTo(event.location(), false);
1482 model_->SelectWord();
1483 double_click_word_ = GetRenderText()->selection();
1484 OnCaretBoundsChanged();
1485 break;
1486 case 2:
1487 model_->SelectAll(false);
1488 OnCaretBoundsChanged();
1489 break;
1490 default:
1491 NOTREACHED();
1492 }
1493 SchedulePaint();
1494 }
1495
ImeEditingAllowed() const1496 bool NativeTextfieldViews::ImeEditingAllowed() const {
1497 // We don't allow the input method to retrieve or delete content from a
1498 // password field.
1499 ui::TextInputType t = GetTextInputType();
1500 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
1501 }
1502
1503 // static
ShouldInsertChar(char16 ch,int flags)1504 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) {
1505 // Filter out all control characters, including tab and new line characters,
1506 // and all characters with Alt modifier. But we need to allow characters with
1507 // AltGr modifier.
1508 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different
1509 // flag that we don't care about.
1510 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1511 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN;
1512 }
1513
CreateTouchSelectionControllerAndNotifyIt()1514 void NativeTextfieldViews::CreateTouchSelectionControllerAndNotifyIt() {
1515 if (!touch_selection_controller_) {
1516 touch_selection_controller_.reset(
1517 ui::TouchSelectionController::create(this));
1518 }
1519 if (touch_selection_controller_)
1520 touch_selection_controller_->SelectionChanged();
1521 }
1522
PlatformGestureEventHandling(const ui::GestureEvent * event)1523 void NativeTextfieldViews::PlatformGestureEventHandling(
1524 const ui::GestureEvent* event) {
1525 #if defined(OS_WIN) && defined(USE_AURA)
1526 if (event->type() == ui::ET_GESTURE_TAP && !textfield_->read_only())
1527 base::win::DisplayVirtualKeyboard();
1528 #endif
1529 }
1530
RevealObscuredChar(int index,const base::TimeDelta & duration)1531 void NativeTextfieldViews::RevealObscuredChar(int index,
1532 const base::TimeDelta& duration) {
1533 GetRenderText()->SetObscuredRevealIndex(index);
1534 SchedulePaint();
1535
1536 if (index != -1) {
1537 obscured_reveal_timer_.Start(
1538 FROM_HERE,
1539 duration,
1540 base::Bind(&NativeTextfieldViews::RevealObscuredChar,
1541 base::Unretained(this), -1, base::TimeDelta()));
1542 }
1543 }
1544
1545 } // namespace views
1546