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