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 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 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 89 NativeTextfieldViews::~NativeTextfieldViews() { 90 } 91 92 //////////////////////////////////////////////////////////////////////////////// 93 // NativeTextfieldViews, View overrides: 94 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 110 bool NativeTextfieldViews::ExceededDragThresholdFromLastClickLocation( 111 const ui::MouseEvent& event) { 112 return ExceededDragThreshold(event.location() - last_click_location_); 113 } 114 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 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 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 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 250 bool NativeTextfieldViews::OnKeyReleased(const ui::KeyEvent& event) { 251 NOTREACHED(); 252 return false; 253 } 254 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 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 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 297 void NativeTextfieldViews::OnDragExited() { 298 is_drop_cursor_visible_ = false; 299 SchedulePaint(); 300 } 301 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 344 void NativeTextfieldViews::OnDragDone() { 345 initiating_drag_ = false; 346 is_drop_cursor_visible_ = false; 347 } 348 349 void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { 350 OnPaintBackground(canvas); 351 PaintTextAndCursor(canvas); 352 if (textfield_->draw_border()) 353 OnPaintBorder(canvas); 354 } 355 356 void NativeTextfieldViews::OnFocus() { 357 NOTREACHED(); 358 } 359 360 void NativeTextfieldViews::OnBlur() { 361 NOTREACHED(); 362 } 363 364 void NativeTextfieldViews::OnNativeThemeChanged(const ui::NativeTheme* theme) { 365 UpdateColorsFromTheme(theme); 366 } 367 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 386 void NativeTextfieldViews::MoveCaretTo(const gfx::Point& point) { 387 SelectRect(point, point); 388 } 389 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 400 gfx::Rect NativeTextfieldViews::GetBounds() { 401 return bounds(); 402 } 403 404 gfx::NativeView NativeTextfieldViews::GetNativeView() { 405 return GetWidget()->GetNativeView(); 406 } 407 408 void NativeTextfieldViews::ConvertPointToScreen(gfx::Point* point) { 409 View::ConvertPointToScreen(this, point); 410 } 411 412 void NativeTextfieldViews::ConvertPointFromScreen(gfx::Point* point) { 413 View::ConvertPointFromScreen(this, point); 414 } 415 416 bool NativeTextfieldViews::DrawsHandles() { 417 return false; 418 } 419 420 void NativeTextfieldViews::OpenContextMenu(const gfx::Point& anchor) { 421 touch_selection_controller_.reset(); 422 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); 423 } 424 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: 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: 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 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 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 496 string16 NativeTextfieldViews::GetText() const { 497 return model_->GetText(); 498 } 499 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 508 void NativeTextfieldViews::AppendText(const string16& text) { 509 if (text.empty()) 510 return; 511 model_->Append(GetTextForDisplay(text)); 512 OnCaretBoundsChanged(); 513 SchedulePaint(); 514 } 515 516 void NativeTextfieldViews::InsertOrReplaceText(const string16& text) { 517 if (text.empty()) 518 return; 519 model_->InsertText(text); 520 OnCaretBoundsChanged(); 521 SchedulePaint(); 522 } 523 524 base::i18n::TextDirection NativeTextfieldViews::GetTextDirection() const { 525 return GetRenderText()->GetTextDirection(); 526 } 527 528 string16 NativeTextfieldViews::GetSelectedText() const { 529 return model_->GetSelectedText(); 530 } 531 532 void NativeTextfieldViews::SelectAll(bool reversed) { 533 model_->SelectAll(reversed); 534 OnCaretBoundsChanged(); 535 SchedulePaint(); 536 } 537 538 void NativeTextfieldViews::ClearSelection() { 539 model_->ClearSelection(); 540 OnCaretBoundsChanged(); 541 SchedulePaint(); 542 } 543 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 557 void NativeTextfieldViews::UpdateTextColor() { 558 SetColor(textfield_->GetTextColor()); 559 } 560 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 568 void NativeTextfieldViews::UpdateReadOnly() { 569 OnTextInputTypeChanged(); 570 } 571 572 void NativeTextfieldViews::UpdateFont() { 573 GetRenderText()->SetFontList(textfield_->font_list()); 574 OnCaretBoundsChanged(); 575 } 576 577 void NativeTextfieldViews::UpdateIsObscured() { 578 GetRenderText()->SetObscured(textfield_->IsObscured()); 579 OnCaretBoundsChanged(); 580 SchedulePaint(); 581 OnTextInputTypeChanged(); 582 } 583 584 void NativeTextfieldViews::UpdateEnabled() { 585 SetEnabled(textfield_->enabled()); 586 SchedulePaint(); 587 OnTextInputTypeChanged(); 588 } 589 590 gfx::Insets NativeTextfieldViews::CalculateInsets() { 591 return GetInsets(); 592 } 593 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 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 612 bool NativeTextfieldViews::SetFocus() { 613 return false; 614 } 615 616 View* NativeTextfieldViews::GetView() { 617 return this; 618 } 619 620 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { 621 NOTREACHED(); 622 return NULL; 623 } 624 625 bool NativeTextfieldViews::IsIMEComposing() const { 626 return model_->HasCompositionText(); 627 } 628 629 gfx::Range NativeTextfieldViews::GetSelectedRange() const { 630 return GetRenderText()->selection(); 631 } 632 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 641 gfx::SelectionModel NativeTextfieldViews::GetSelectionModel() const { 642 return GetRenderText()->selection_model(); 643 } 644 645 void NativeTextfieldViews::SelectSelectionModel( 646 const gfx::SelectionModel& sel) { 647 model_->SelectSelectionModel(sel); 648 OnCaretBoundsChanged(); 649 SchedulePaint(); 650 } 651 652 size_t NativeTextfieldViews::GetCursorPosition() const { 653 return model_->GetCursorPosition(); 654 } 655 656 bool NativeTextfieldViews::GetCursorEnabled() const { 657 return GetRenderText()->cursor_enabled(); 658 } 659 660 void NativeTextfieldViews::SetCursorEnabled(bool enabled) { 661 GetRenderText()->SetCursorEnabled(enabled); 662 } 663 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 673 bool NativeTextfieldViews::HandleKeyReleased(const ui::KeyEvent& e) { 674 return false; // crbug.com/127520 675 } 676 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 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 707 ui::TextInputClient* NativeTextfieldViews::GetTextInputClient() { 708 return textfield_->read_only() ? NULL : this; 709 } 710 711 void NativeTextfieldViews::ClearEditHistory() { 712 model_->ClearEditHistory(); 713 } 714 715 int NativeTextfieldViews::GetFontHeight() { 716 return GetRenderText()->font_list().GetHeight(); 717 } 718 719 int NativeTextfieldViews::GetTextfieldBaseline() const { 720 return GetRenderText()->GetBaseline(); 721 } 722 723 int NativeTextfieldViews::GetWidthNeededForText() const { 724 return GetRenderText()->GetContentWidth() + GetInsets().width(); 725 } 726 727 void NativeTextfieldViews::ExecuteTextCommand(int command_id) { 728 ExecuteCommand(command_id, 0); 729 } 730 731 bool NativeTextfieldViews::HasTextBeingDragged() { 732 return initiating_drag_; 733 } 734 735 gfx::Point NativeTextfieldViews::GetContextMenuLocation() { 736 return GetCaretBounds().bottom_right(); 737 } 738 739 ///////////////////////////////////////////////////////////////// 740 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: 741 742 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { 743 return true; 744 } 745 746 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 773 bool NativeTextfieldViews::GetAcceleratorForCommandId(int command_id, 774 ui::Accelerator* accelerator) { 775 return false; 776 } 777 778 bool NativeTextfieldViews::IsItemForCommandIdDynamic(int command_id) const { 779 const TextfieldController* controller = textfield_->GetController(); 780 return controller && controller->IsItemForCommandIdDynamic(command_id); 781 } 782 783 string16 NativeTextfieldViews::GetLabelForCommandId(int command_id) const { 784 const TextfieldController* controller = textfield_->GetController(); 785 return controller ? controller->GetLabelForCommandId(command_id) : string16(); 786 } 787 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 841 void NativeTextfieldViews::SetColor(SkColor value) { 842 GetRenderText()->SetColor(value); 843 SchedulePaint(); 844 } 845 846 void NativeTextfieldViews::ApplyColor(SkColor value, const gfx::Range& range) { 847 GetRenderText()->ApplyColor(value, range); 848 SchedulePaint(); 849 } 850 851 void NativeTextfieldViews::SetStyle(gfx::TextStyle style, bool value) { 852 GetRenderText()->SetStyle(style, value); 853 SchedulePaint(); 854 } 855 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 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 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 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 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 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 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 960 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 975 ui::TextInputType NativeTextfieldViews::GetTextInputType() const { 976 return textfield_->GetTextInputType(); 977 } 978 979 ui::TextInputMode NativeTextfieldViews::GetTextInputMode() const { 980 return ui::TEXT_INPUT_MODE_DEFAULT; 981 } 982 983 bool NativeTextfieldViews::CanComposeInline() const { 984 return true; 985 } 986 987 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 995 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 1020 bool NativeTextfieldViews::HasCompositionText() const { 1021 return model_->HasCompositionText(); 1022 } 1023 1024 bool NativeTextfieldViews::GetTextRange(gfx::Range* range) const { 1025 if (!ImeEditingAllowed()) 1026 return false; 1027 1028 model_->GetTextRange(range); 1029 return true; 1030 } 1031 1032 bool NativeTextfieldViews::GetCompositionTextRange(gfx::Range* range) const { 1033 if (!ImeEditingAllowed()) 1034 return false; 1035 1036 model_->GetCompositionTextRange(range); 1037 return true; 1038 } 1039 1040 bool NativeTextfieldViews::GetSelectionRange(gfx::Range* range) const { 1041 if (!ImeEditingAllowed()) 1042 return false; 1043 *range = GetSelectedRange(); 1044 return true; 1045 } 1046 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 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 1071 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 1085 void NativeTextfieldViews::OnInputMethodChanged() { 1086 // TODO(msw): NOTIMPLEMENTED(); see http://crbug.com/140402 1087 } 1088 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 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 1117 void NativeTextfieldViews::EnsureCaretInRect(const gfx::Rect& rect) { 1118 } 1119 1120 void NativeTextfieldViews::OnCandidateWindowShown() { 1121 } 1122 1123 void NativeTextfieldViews::OnCandidateWindowUpdated() { 1124 } 1125 1126 void NativeTextfieldViews::OnCandidateWindowHidden() { 1127 } 1128 1129 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { 1130 if (skip_input_method_cancel_composition_) 1131 return; 1132 DCHECK(textfield_->GetInputMethod()); 1133 textfield_->GetInputMethod()->CancelComposition(textfield_); 1134 } 1135 1136 gfx::RenderText* NativeTextfieldViews::GetRenderText() const { 1137 return model_->render_text(); 1138 } 1139 1140 string16 NativeTextfieldViews::GetTextForDisplay(const string16& text) { 1141 return textfield_->style() & Textfield::STYLE_LOWERCASE ? 1142 base::i18n::ToLower(text) : text; 1143 } 1144 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 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 1169 void NativeTextfieldViews::RepaintCursor() { 1170 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); 1171 r.Inset(-1, -1, -1, -1); 1172 SchedulePaintInRect(r); 1173 } 1174 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 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 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 1325 void NativeTextfieldViews::PropagateTextChange() { 1326 textfield_->SyncText(); 1327 } 1328 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 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 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 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 1393 void NativeTextfieldViews::OnBeforeUserAction() { 1394 TextfieldController* controller = textfield_->GetController(); 1395 if (controller) 1396 controller->OnBeforeUserAction(textfield_); 1397 } 1398 1399 void NativeTextfieldViews::OnAfterUserAction() { 1400 TextfieldController* controller = textfield_->GetController(); 1401 if (controller) 1402 controller->OnAfterUserAction(textfield_); 1403 } 1404 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 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 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 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 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 1496 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 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 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 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 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