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