Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "views/controls/textfield/textfield_views_model.h" | 5 #include "views/controls/textfield/textfield_views_model.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 362 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 373 render_text_->MoveCursorLeft(break_type, select); | 373 render_text_->MoveCursorLeft(break_type, select); |
| 374 } | 374 } |
| 375 | 375 |
| 376 void TextfieldViewsModel::MoveCursorRight(gfx::BreakType break_type, | 376 void TextfieldViewsModel::MoveCursorRight(gfx::BreakType break_type, |
| 377 bool select) { | 377 bool select) { |
| 378 if (HasCompositionText()) | 378 if (HasCompositionText()) |
| 379 ConfirmCompositionText(); | 379 ConfirmCompositionText(); |
| 380 render_text_->MoveCursorRight(break_type, select); | 380 render_text_->MoveCursorRight(break_type, select); |
| 381 } | 381 } |
| 382 | 382 |
| 383 bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) { | 383 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { |
| 384 if (HasCompositionText()) | 384 if (HasCompositionText()) { |
| 385 ConfirmCompositionText(); | 385 ConfirmCompositionText(); |
| 386 return render_text_->MoveCursorTo(pos, select); | 386 // ConfirmCompositionText() updates cursor position. Need to reflect it in |
| 387 // the SelectionModel parameter of MoveCursorTo(). | |
| 388 gfx::SelectionModel sel(selection); | |
| 389 sel.set_selection_start(render_text_->GetSelectionStart()); | |
| 390 return render_text_->MoveCursorTo(sel); | |
| 391 } | |
| 392 return render_text_->MoveCursorTo(selection); | |
| 387 } | 393 } |
| 388 | 394 |
| 389 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 395 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
| 390 if (HasCompositionText()) | 396 if (HasCompositionText()) |
| 391 ConfirmCompositionText(); | 397 ConfirmCompositionText(); |
| 392 return render_text_->MoveCursorTo(point, select); | 398 return render_text_->MoveCursorTo(point, select); |
| 393 } | 399 } |
| 394 | 400 |
| 395 std::vector<gfx::Rect> TextfieldViewsModel::GetSelectionBounds() const { | 401 std::vector<gfx::Rect> TextfieldViewsModel::GetSelectionBounds() const { |
| 396 return render_text_->GetSubstringBounds(render_text_->GetSelection()); | 402 return render_text_->GetSubstringBounds(render_text_->GetSelectionStart(), |
| 403 render_text_->GetCursorPosition()); | |
| 397 } | 404 } |
| 398 | 405 |
| 399 string16 TextfieldViewsModel::GetSelectedText() const { | 406 string16 TextfieldViewsModel::GetSelectedText() const { |
| 400 ui::Range selection = render_text_->GetSelection(); | 407 return GetText().substr(render_text_->MinOfSelection(), |
| 401 return GetText().substr(selection.GetMin(), selection.length()); | 408 std::abs(static_cast<long>(render_text_->GetCursorPosition() - |
|
msw
2011/08/03 19:46:39
It just occurred to me, can't we safely use MaxOfS
xji
2011/08/03 22:40:32
Done.
| |
| 409 render_text_->GetSelectionStart()))); | |
| 402 } | 410 } |
| 403 | 411 |
| 404 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { | 412 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { |
| 405 *range = render_text_->GetSelection(); | 413 range->set_start(render_text_->GetSelectionStart()); |
| 414 range->set_end(render_text_->GetCursorPosition()); | |
| 406 } | 415 } |
| 407 | 416 |
| 408 void TextfieldViewsModel::SelectRange(const ui::Range& range) { | 417 void TextfieldViewsModel::SelectRange(const ui::Range& range) { |
| 409 if (HasCompositionText()) | 418 if (HasCompositionText()) |
| 410 ConfirmCompositionText(); | 419 ConfirmCompositionText(); |
| 411 render_text_->SetSelection(range); | 420 gfx::SelectionModel selection(range.start(), range.end(), |
| 421 range.end(), gfx::SelectionModel::PREVIOUS_GRAPHEME_TRAILING); | |
| 422 render_text_->SetSelectionModel(selection); | |
|
msw
2011/08/03 19:46:39
call TextfieldViewsModel::SelectSelectionModel ins
xji
2011/08/03 22:40:32
Done.
| |
| 423 } | |
| 424 | |
| 425 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { | |
| 426 if (HasCompositionText()) | |
| 427 ConfirmCompositionText(); | |
| 428 render_text_->SetSelectionModel(sel); | |
| 412 } | 429 } |
| 413 | 430 |
| 414 void TextfieldViewsModel::SelectAll() { | 431 void TextfieldViewsModel::SelectAll() { |
| 415 if (HasCompositionText()) | 432 if (HasCompositionText()) |
| 416 ConfirmCompositionText(); | 433 ConfirmCompositionText(); |
| 417 render_text_->SelectAll(); | 434 render_text_->SelectAll(); |
| 418 } | 435 } |
| 419 | 436 |
| 420 void TextfieldViewsModel::SelectWord() { | 437 void TextfieldViewsModel::SelectWord() { |
| 421 if (HasCompositionText()) | 438 if (HasCompositionText()) |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 485 | 502 |
| 486 bool TextfieldViewsModel::Cut() { | 503 bool TextfieldViewsModel::Cut() { |
| 487 if (!HasCompositionText() && HasSelection()) { | 504 if (!HasCompositionText() && HasSelection()) { |
| 488 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 505 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
| 489 ->GetClipboard()).WriteText(GetSelectedText()); | 506 ->GetClipboard()).WriteText(GetSelectedText()); |
| 490 // A trick to let undo/redo handle cursor correctly. | 507 // A trick to let undo/redo handle cursor correctly. |
| 491 // Undoing CUT moves the cursor to the end of the change rather | 508 // Undoing CUT moves the cursor to the end of the change rather |
| 492 // than beginning, unlike Delete/Backspace. | 509 // than beginning, unlike Delete/Backspace. |
| 493 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 510 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
| 494 // update DeleteEdit and remove this trick. | 511 // update DeleteEdit and remove this trick. |
| 495 ui::Range selection = render_text_->GetSelection(); | 512 gfx::SelectionModel sel(render_text_->GetCursorPosition(), |
| 496 render_text_->SetSelection(ui::Range(selection.end(), selection.start())); | 513 render_text_->GetSelectionStart(), |
| 514 render_text_->GetSelectionStart(), | |
| 515 gfx::SelectionModel::LEADING); | |
| 516 render_text_->SetSelectionModel(sel); | |
| 497 DeleteSelection(); | 517 DeleteSelection(); |
| 498 return true; | 518 return true; |
| 499 } | 519 } |
| 500 return false; | 520 return false; |
| 501 } | 521 } |
| 502 | 522 |
| 503 void TextfieldViewsModel::Copy() { | 523 void TextfieldViewsModel::Copy() { |
| 504 if (!HasCompositionText() && HasSelection()) { | 524 if (!HasCompositionText() && HasSelection()) { |
| 505 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 525 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
| 506 ->GetClipboard()).WriteText(GetSelectedText()); | 526 ->GetClipboard()).WriteText(GetSelectedText()); |
| 507 } | 527 } |
| 508 } | 528 } |
| 509 | 529 |
| 510 bool TextfieldViewsModel::Paste() { | 530 bool TextfieldViewsModel::Paste() { |
| 511 string16 result; | 531 string16 result; |
| 512 views::ViewsDelegate::views_delegate->GetClipboard() | 532 views::ViewsDelegate::views_delegate->GetClipboard() |
| 513 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); | 533 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); |
| 514 if (!result.empty()) { | 534 if (!result.empty()) { |
| 515 InsertTextInternal(result, false); | 535 InsertTextInternal(result, false); |
| 516 return true; | 536 return true; |
| 517 } | 537 } |
| 518 return false; | 538 return false; |
| 519 } | 539 } |
| 520 | 540 |
| 521 bool TextfieldViewsModel::HasSelection() const { | 541 bool TextfieldViewsModel::HasSelection() const { |
| 522 return !render_text_->GetSelection().is_empty(); | 542 return !render_text_->EmptySelection(); |
| 523 } | 543 } |
| 524 | 544 |
| 525 void TextfieldViewsModel::DeleteSelection() { | 545 void TextfieldViewsModel::DeleteSelection() { |
| 526 DCHECK(!HasCompositionText()); | 546 DCHECK(!HasCompositionText()); |
| 527 DCHECK(HasSelection()); | 547 DCHECK(HasSelection()); |
| 528 ui::Range selection = render_text_->GetSelection(); | 548 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), |
| 529 ExecuteAndRecordDelete(selection.start(), selection.end(), false); | 549 render_text_->GetCursorPosition(), false); |
| 530 } | 550 } |
| 531 | 551 |
| 532 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 552 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| 533 const string16& text, size_t position) { | 553 const string16& text, size_t position) { |
| 534 if (HasCompositionText()) | 554 if (HasCompositionText()) |
| 535 CancelCompositionText(); | 555 CancelCompositionText(); |
| 536 ExecuteAndRecordReplace(DO_NOT_MERGE, | 556 ExecuteAndRecordReplace(DO_NOT_MERGE, |
| 537 GetCursorPosition(), | 557 GetCursorPosition(), |
| 538 position + text.length(), | 558 position + text.length(), |
| 539 text, | 559 text, |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 560 if (composition.text.empty()) | 580 if (composition.text.empty()) |
| 561 return; | 581 return; |
| 562 | 582 |
| 563 size_t cursor = GetCursorPosition(); | 583 size_t cursor = GetCursorPosition(); |
| 564 string16 new_text = GetText(); | 584 string16 new_text = GetText(); |
| 565 render_text_->SetText(new_text.insert(cursor, composition.text)); | 585 render_text_->SetText(new_text.insert(cursor, composition.text)); |
| 566 ui::Range range(cursor, cursor + composition.text.length()); | 586 ui::Range range(cursor, cursor + composition.text.length()); |
| 567 render_text_->SetCompositionRange(range); | 587 render_text_->SetCompositionRange(range); |
| 568 // TODO(msw): Support multiple composition underline ranges. | 588 // TODO(msw): Support multiple composition underline ranges. |
| 569 | 589 |
| 570 if (composition.selection.IsValid()) | 590 if (composition.selection.IsValid()) { |
| 571 render_text_->SetSelection(ui::Range( | 591 size_t start = std::min(range.start() + composition.selection.start(), |
| 572 std::min(range.start() + composition.selection.start(), range.end()), | 592 range.end()); |
| 573 std::min(range.start() + composition.selection.end(), range.end()))); | 593 size_t end = std::min(range.start() + composition.selection.end(), |
| 574 else | 594 range.end()); |
| 595 gfx::SelectionModel sel(start, end, end, | |
| 596 gfx::SelectionModel::PREVIOUS_GRAPHEME_TRAILING); | |
| 597 render_text_->SetSelectionModel(sel); | |
| 598 } else { | |
| 575 render_text_->SetCursorPosition(range.end()); | 599 render_text_->SetCursorPosition(range.end()); |
| 600 } | |
| 576 } | 601 } |
| 577 | 602 |
| 578 void TextfieldViewsModel::ConfirmCompositionText() { | 603 void TextfieldViewsModel::ConfirmCompositionText() { |
| 579 DCHECK(HasCompositionText()); | 604 DCHECK(HasCompositionText()); |
| 580 ui::Range range = render_text_->GetCompositionRange(); | 605 ui::Range range = render_text_->GetCompositionRange(); |
| 581 string16 text = GetText().substr(range.start(), range.length()); | 606 string16 text = GetText().substr(range.start(), range.length()); |
| 582 // TODO(oshima): current behavior on ChromeOS is a bit weird and not | 607 // TODO(oshima): current behavior on ChromeOS is a bit weird and not |
| 583 // sure exactly how this should work. Find out and fix if necessary. | 608 // sure exactly how this should work. Find out and fix if necessary. |
| 584 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); | 609 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
| 585 render_text_->SetCursorPosition(range.end()); | 610 render_text_->SetCursorPosition(range.end()); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 633 ExecuteAndRecordInsert(text, mergeable); | 658 ExecuteAndRecordInsert(text, mergeable); |
| 634 } | 659 } |
| 635 } | 660 } |
| 636 | 661 |
| 637 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, | 662 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, |
| 638 bool mergeable) { | 663 bool mergeable) { |
| 639 if (HasCompositionText()) { | 664 if (HasCompositionText()) { |
| 640 CancelCompositionText(); | 665 CancelCompositionText(); |
| 641 } else if (!HasSelection()) { | 666 } else if (!HasSelection()) { |
| 642 size_t cursor = GetCursorPosition(); | 667 size_t cursor = GetCursorPosition(); |
| 643 render_text_->SetSelection(ui::Range(cursor + text.length(), cursor)); | 668 gfx::SelectionModel sel(render_text_->selection_model()); |
| 669 sel.set_selection_start(cursor + text.length()); | |
| 670 render_text_->SetSelectionModel(sel); | |
| 644 } | 671 } |
| 645 // Edit history is recorded in InsertText. | 672 // Edit history is recorded in InsertText. |
| 646 InsertTextInternal(text, mergeable); | 673 InsertTextInternal(text, mergeable); |
| 647 } | 674 } |
| 648 | 675 |
| 649 void TextfieldViewsModel::ClearEditHistory() { | 676 void TextfieldViewsModel::ClearEditHistory() { |
| 650 STLDeleteContainerPointers(edit_history_.begin(), | 677 STLDeleteContainerPointers(edit_history_.begin(), |
| 651 edit_history_.end()); | 678 edit_history_.end()); |
| 652 edit_history_.clear(); | 679 edit_history_.clear(); |
| 653 current_edit_ = edit_history_.end(); | 680 current_edit_ = edit_history_.end(); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 664 delete_start++; | 691 delete_start++; |
| 665 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 692 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
| 666 edit_history_.erase(delete_start, edit_history_.end()); | 693 edit_history_.erase(delete_start, edit_history_.end()); |
| 667 } | 694 } |
| 668 | 695 |
| 669 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, | 696 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, |
| 670 size_t to, | 697 size_t to, |
| 671 bool mergeable) { | 698 bool mergeable) { |
| 672 size_t old_text_start = std::min(from, to); | 699 size_t old_text_start = std::min(from, to); |
| 673 const string16 text = GetText().substr(old_text_start, | 700 const string16 text = GetText().substr(old_text_start, |
| 674 std::abs(static_cast<long>(from - to))); | 701 std::abs(static_cast<long>(from - to))); |
| 675 bool backward = from > to; | 702 bool backward = from > to; |
| 676 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 703 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
| 677 bool delete_edit = AddOrMergeEditHistory(edit); | 704 bool delete_edit = AddOrMergeEditHistory(edit); |
| 678 edit->Redo(this); | 705 edit->Redo(this); |
| 679 if (delete_edit) | 706 if (delete_edit) |
| 680 delete edit; | 707 delete edit; |
| 681 } | 708 } |
| 682 | 709 |
| 683 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 710 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
| 684 MergeType merge_type, const string16& new_text) { | 711 MergeType merge_type, const string16& new_text) { |
| 685 size_t new_text_start = render_text_->GetSelection().GetMin(); | 712 size_t new_text_start = render_text_->MinOfSelection(); |
| 686 size_t new_cursor_pos = new_text_start + new_text.length(); | 713 size_t new_cursor_pos = new_text_start + new_text.length(); |
| 687 ExecuteAndRecordReplace(merge_type, | 714 ExecuteAndRecordReplace(merge_type, |
| 688 GetCursorPosition(), | 715 GetCursorPosition(), |
| 689 new_cursor_pos, | 716 new_cursor_pos, |
| 690 new_text, | 717 new_text, |
| 691 new_text_start); | 718 new_text_start); |
| 692 } | 719 } |
| 693 | 720 |
| 694 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, | 721 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, |
| 695 size_t old_cursor_pos, | 722 size_t old_cursor_pos, |
| 696 size_t new_cursor_pos, | 723 size_t new_cursor_pos, |
| 697 const string16& new_text, | 724 const string16& new_text, |
| 698 size_t new_text_start) { | 725 size_t new_text_start) { |
| 699 size_t old_text_start = render_text_->GetSelection().GetMin(); | 726 size_t old_text_start = render_text_->MinOfSelection(); |
| 700 bool backward = render_text_->GetSelection().is_reversed(); | 727 bool backward = render_text_->GetSelectionStart() > |
| 728 render_text_->GetCursorPosition(); | |
| 701 Edit* edit = new ReplaceEdit(merge_type, | 729 Edit* edit = new ReplaceEdit(merge_type, |
| 702 GetSelectedText(), | 730 GetSelectedText(), |
| 703 old_cursor_pos, | 731 old_cursor_pos, |
| 704 old_text_start, | 732 old_text_start, |
| 705 backward, | 733 backward, |
| 706 new_cursor_pos, | 734 new_cursor_pos, |
| 707 new_text, | 735 new_text, |
| 708 new_text_start); | 736 new_text_start); |
| 709 bool delete_edit = AddOrMergeEditHistory(edit); | 737 bool delete_edit = AddOrMergeEditHistory(edit); |
| 710 edit->Redo(this); | 738 edit->Redo(this); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 752 if (delete_from != delete_to) | 780 if (delete_from != delete_to) |
| 753 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 781 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
| 754 if (!new_text.empty()) | 782 if (!new_text.empty()) |
| 755 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 783 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
| 756 render_text_->SetCursorPosition(new_cursor_pos); | 784 render_text_->SetCursorPosition(new_cursor_pos); |
| 757 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 785 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). |
| 758 // This looks fine feature and we may want to do the same. | 786 // This looks fine feature and we may want to do the same. |
| 759 } | 787 } |
| 760 | 788 |
| 761 } // namespace views | 789 } // namespace views |
| OLD | NEW |