| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "ui/views/controls/textfield/textfield_views_model.h" | 5 #include "ui/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 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 return true; | 339 return true; |
| 340 } | 340 } |
| 341 if (HasSelection()) { | 341 if (HasSelection()) { |
| 342 DeleteSelection(); | 342 DeleteSelection(); |
| 343 return true; | 343 return true; |
| 344 } | 344 } |
| 345 if (GetText().length() > GetCursorPosition()) { | 345 if (GetText().length() > GetCursorPosition()) { |
| 346 size_t cursor_position = GetCursorPosition(); | 346 size_t cursor_position = GetCursorPosition(); |
| 347 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( | 347 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( |
| 348 cursor_position, gfx::CURSOR_FORWARD); | 348 cursor_position, gfx::CURSOR_FORWARD); |
| 349 ExecuteAndRecordDelete(cursor_position, next_grapheme_index, true); | 349 ExecuteAndRecordDelete(ui::Range(cursor_position, next_grapheme_index), |
| 350 true); |
| 350 return true; | 351 return true; |
| 351 } | 352 } |
| 352 return false; | 353 return false; |
| 353 } | 354 } |
| 354 | 355 |
| 355 bool TextfieldViewsModel::Backspace() { | 356 bool TextfieldViewsModel::Backspace() { |
| 356 if (HasCompositionText()) { | 357 if (HasCompositionText()) { |
| 357 // No undo/redo for composition text. | 358 // No undo/redo for composition text. |
| 358 CancelCompositionText(); | 359 CancelCompositionText(); |
| 359 return true; | 360 return true; |
| 360 } | 361 } |
| 361 if (HasSelection()) { | 362 if (HasSelection()) { |
| 362 DeleteSelection(); | 363 DeleteSelection(); |
| 363 return true; | 364 return true; |
| 364 } | 365 } |
| 365 size_t cursor_position = GetCursorPosition(); | 366 size_t cursor_position = GetCursorPosition(); |
| 366 if (cursor_position > 0) { | 367 if (cursor_position > 0) { |
| 367 // Delete one code point, which may be two UTF-16 words. | 368 // Delete one code point, which may be two UTF-16 words. |
| 368 size_t previous_char = | 369 size_t previous_char = |
| 369 ui::UTF16OffsetToIndex(GetText(), cursor_position, -1); | 370 ui::UTF16OffsetToIndex(GetText(), cursor_position, -1); |
| 370 ExecuteAndRecordDelete(cursor_position, previous_char, true); | 371 ExecuteAndRecordDelete(ui::Range(cursor_position, previous_char), true); |
| 371 return true; | 372 return true; |
| 372 } | 373 } |
| 373 return false; | 374 return false; |
| 374 } | 375 } |
| 375 | 376 |
| 376 size_t TextfieldViewsModel::GetCursorPosition() const { | 377 size_t TextfieldViewsModel::GetCursorPosition() const { |
| 377 return render_text_->GetCursorPosition(); | 378 return render_text_->cursor_position(); |
| 378 } | 379 } |
| 379 | 380 |
| 380 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, | 381 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, |
| 381 gfx::VisualCursorDirection direction, | 382 gfx::VisualCursorDirection direction, |
| 382 bool select) { | 383 bool select) { |
| 383 if (HasCompositionText()) | 384 if (HasCompositionText()) |
| 384 ConfirmCompositionText(); | 385 ConfirmCompositionText(); |
| 385 render_text_->MoveCursor(break_type, direction, select); | 386 render_text_->MoveCursor(break_type, direction, select); |
| 386 } | 387 } |
| 387 | 388 |
| 388 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { | 389 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& model) { |
| 389 if (HasCompositionText()) { | 390 if (HasCompositionText()) { |
| 390 ConfirmCompositionText(); | 391 ConfirmCompositionText(); |
| 391 // ConfirmCompositionText() updates cursor position. Need to reflect it in | 392 // ConfirmCompositionText() updates cursor position. Need to reflect it in |
| 392 // the SelectionModel parameter of MoveCursorTo(). | 393 // the SelectionModel parameter of MoveCursorTo(). |
| 393 if (render_text_->GetSelectionStart() != selection.selection_end()) | 394 ui::Range range(render_text_->selection().start(), model.caret_pos()); |
| 394 return render_text_->SelectRange(ui::Range( | 395 if (!range.is_empty()) |
| 395 render_text_->GetSelectionStart(), selection.selection_end())); | 396 return render_text_->SelectRange(range); |
| 396 gfx::SelectionModel sel(selection.selection_end(), | 397 return render_text_->MoveCursorTo( |
| 397 selection.caret_pos(), | 398 gfx::SelectionModel(model.caret_pos(), model.caret_affinity())); |
| 398 selection.caret_placement()); | |
| 399 return render_text_->MoveCursorTo(sel); | |
| 400 } | 399 } |
| 401 return render_text_->MoveCursorTo(selection); | 400 return render_text_->MoveCursorTo(model); |
| 402 } | 401 } |
| 403 | 402 |
| 404 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 403 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
| 405 if (HasCompositionText()) | 404 if (HasCompositionText()) |
| 406 ConfirmCompositionText(); | 405 ConfirmCompositionText(); |
| 407 return render_text_->MoveCursorTo(point, select); | 406 return render_text_->MoveCursorTo(point, select); |
| 408 } | 407 } |
| 409 | 408 |
| 410 string16 TextfieldViewsModel::GetSelectedText() const { | 409 string16 TextfieldViewsModel::GetSelectedText() const { |
| 411 return GetText().substr(render_text_->MinOfSelection(), | 410 return GetText().substr(render_text_->selection().GetMin(), |
| 412 (render_text_->MaxOfSelection() - render_text_->MinOfSelection())); | 411 render_text_->selection().length()); |
| 413 } | |
| 414 | |
| 415 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { | |
| 416 range->set_start(render_text_->GetSelectionStart()); | |
| 417 range->set_end(render_text_->GetCursorPosition()); | |
| 418 } | 412 } |
| 419 | 413 |
| 420 void TextfieldViewsModel::SelectRange(const ui::Range& range) { | 414 void TextfieldViewsModel::SelectRange(const ui::Range& range) { |
| 421 if (HasCompositionText()) | 415 if (HasCompositionText()) |
| 422 ConfirmCompositionText(); | 416 ConfirmCompositionText(); |
| 423 render_text_->SelectRange(range); | 417 render_text_->SelectRange(range); |
| 424 } | 418 } |
| 425 | 419 |
| 426 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { | 420 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { |
| 427 *sel = render_text_->selection_model(); | 421 *sel = render_text_->selection_model(); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 504 bool TextfieldViewsModel::Cut() { | 498 bool TextfieldViewsModel::Cut() { |
| 505 if (!HasCompositionText() && HasSelection() && !render_text_->is_obscured()) { | 499 if (!HasCompositionText() && HasSelection() && !render_text_->is_obscured()) { |
| 506 ui::ScopedClipboardWriter( | 500 ui::ScopedClipboardWriter( |
| 507 views::ViewsDelegate::views_delegate->GetClipboard(), | 501 views::ViewsDelegate::views_delegate->GetClipboard(), |
| 508 ui::Clipboard::BUFFER_STANDARD).WriteText(GetSelectedText()); | 502 ui::Clipboard::BUFFER_STANDARD).WriteText(GetSelectedText()); |
| 509 // A trick to let undo/redo handle cursor correctly. | 503 // A trick to let undo/redo handle cursor correctly. |
| 510 // Undoing CUT moves the cursor to the end of the change rather | 504 // Undoing CUT moves the cursor to the end of the change rather |
| 511 // than beginning, unlike Delete/Backspace. | 505 // than beginning, unlike Delete/Backspace. |
| 512 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 506 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
| 513 // update DeleteEdit and remove this trick. | 507 // update DeleteEdit and remove this trick. |
| 514 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), | 508 const ui::Range& selection = render_text_->selection(); |
| 515 render_text_->GetSelectionStart())); | 509 render_text_->SelectRange(ui::Range(selection.end(), selection.start())); |
| 516 DeleteSelection(); | 510 DeleteSelection(); |
| 517 return true; | 511 return true; |
| 518 } | 512 } |
| 519 return false; | 513 return false; |
| 520 } | 514 } |
| 521 | 515 |
| 522 bool TextfieldViewsModel::Copy() { | 516 bool TextfieldViewsModel::Copy() { |
| 523 if (!HasCompositionText() && HasSelection() && !render_text_->is_obscured()) { | 517 if (!HasCompositionText() && HasSelection() && !render_text_->is_obscured()) { |
| 524 ui::ScopedClipboardWriter( | 518 ui::ScopedClipboardWriter( |
| 525 views::ViewsDelegate::views_delegate->GetClipboard(), | 519 views::ViewsDelegate::views_delegate->GetClipboard(), |
| 526 ui::Clipboard::BUFFER_STANDARD).WriteText(GetSelectedText()); | 520 ui::Clipboard::BUFFER_STANDARD).WriteText(GetSelectedText()); |
| 527 return true; | 521 return true; |
| 528 } | 522 } |
| 529 return false; | 523 return false; |
| 530 } | 524 } |
| 531 | 525 |
| 532 bool TextfieldViewsModel::Paste() { | 526 bool TextfieldViewsModel::Paste() { |
| 533 string16 result; | 527 string16 result; |
| 534 views::ViewsDelegate::views_delegate->GetClipboard() | 528 views::ViewsDelegate::views_delegate->GetClipboard() |
| 535 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); | 529 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); |
| 536 if (!result.empty()) { | 530 if (!result.empty()) { |
| 537 InsertTextInternal(result, false); | 531 InsertTextInternal(result, false); |
| 538 return true; | 532 return true; |
| 539 } | 533 } |
| 540 return false; | 534 return false; |
| 541 } | 535 } |
| 542 | 536 |
| 543 bool TextfieldViewsModel::HasSelection() const { | 537 bool TextfieldViewsModel::HasSelection() const { |
| 544 return !render_text_->EmptySelection(); | 538 return !render_text_->selection().is_empty(); |
| 545 } | 539 } |
| 546 | 540 |
| 547 void TextfieldViewsModel::DeleteSelection() { | 541 void TextfieldViewsModel::DeleteSelection() { |
| 548 DCHECK(!HasCompositionText()); | 542 DCHECK(!HasCompositionText()); |
| 549 DCHECK(HasSelection()); | 543 DCHECK(HasSelection()); |
| 550 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), | 544 ExecuteAndRecordDelete(render_text_->selection(), false); |
| 551 render_text_->GetCursorPosition(), false); | |
| 552 } | 545 } |
| 553 | 546 |
| 554 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 547 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| 555 const string16& text, size_t position) { | 548 const string16& text, size_t position) { |
| 556 if (HasCompositionText()) | 549 if (HasCompositionText()) |
| 557 CancelCompositionText(); | 550 CancelCompositionText(); |
| 558 ExecuteAndRecordReplace(DO_NOT_MERGE, | 551 ExecuteAndRecordReplace(DO_NOT_MERGE, |
| 559 GetCursorPosition(), | 552 GetCursorPosition(), |
| 560 position + text.length(), | 553 position + text.length(), |
| 561 text, | 554 text, |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 656 bool mergeable) { | 649 bool mergeable) { |
| 657 if (HasCompositionText()) { | 650 if (HasCompositionText()) { |
| 658 CancelCompositionText(); | 651 CancelCompositionText(); |
| 659 } else if (!HasSelection()) { | 652 } else if (!HasSelection()) { |
| 660 size_t cursor = GetCursorPosition(); | 653 size_t cursor = GetCursorPosition(); |
| 661 const gfx::SelectionModel& model = render_text_->selection_model(); | 654 const gfx::SelectionModel& model = render_text_->selection_model(); |
| 662 // When there is no selection, the default is to replace the next grapheme | 655 // When there is no selection, the default is to replace the next grapheme |
| 663 // with |text|. So, need to find the index of next grapheme first. | 656 // with |text|. So, need to find the index of next grapheme first. |
| 664 size_t next = | 657 size_t next = |
| 665 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); | 658 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); |
| 666 if (next == model.selection_end()) | 659 if (next == model.caret_pos()) |
| 667 render_text_->MoveCursorTo(model); | 660 render_text_->MoveCursorTo(model); |
| 668 else | 661 else |
| 669 render_text_->SelectRange(ui::Range(next, model.selection_end())); | 662 render_text_->SelectRange(ui::Range(next, model.caret_pos())); |
| 670 } | 663 } |
| 671 // Edit history is recorded in InsertText. | 664 // Edit history is recorded in InsertText. |
| 672 InsertTextInternal(text, mergeable); | 665 InsertTextInternal(text, mergeable); |
| 673 } | 666 } |
| 674 | 667 |
| 675 void TextfieldViewsModel::ClearEditHistory() { | 668 void TextfieldViewsModel::ClearEditHistory() { |
| 676 STLDeleteContainerPointers(edit_history_.begin(), | 669 STLDeleteContainerPointers(edit_history_.begin(), |
| 677 edit_history_.end()); | 670 edit_history_.end()); |
| 678 edit_history_.clear(); | 671 edit_history_.clear(); |
| 679 current_edit_ = edit_history_.end(); | 672 current_edit_ = edit_history_.end(); |
| 680 } | 673 } |
| 681 | 674 |
| 682 void TextfieldViewsModel::ClearRedoHistory() { | 675 void TextfieldViewsModel::ClearRedoHistory() { |
| 683 if (edit_history_.begin() == edit_history_.end()) | 676 if (edit_history_.begin() == edit_history_.end()) |
| 684 return; | 677 return; |
| 685 if (current_edit_ == edit_history_.end()) { | 678 if (current_edit_ == edit_history_.end()) { |
| 686 ClearEditHistory(); | 679 ClearEditHistory(); |
| 687 return; | 680 return; |
| 688 } | 681 } |
| 689 EditHistory::iterator delete_start = current_edit_; | 682 EditHistory::iterator delete_start = current_edit_; |
| 690 delete_start++; | 683 delete_start++; |
| 691 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 684 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
| 692 edit_history_.erase(delete_start, edit_history_.end()); | 685 edit_history_.erase(delete_start, edit_history_.end()); |
| 693 } | 686 } |
| 694 | 687 |
| 695 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, | 688 void TextfieldViewsModel::ExecuteAndRecordDelete(ui::Range range, |
| 696 size_t to, | |
| 697 bool mergeable) { | 689 bool mergeable) { |
| 698 size_t old_text_start = std::min(from, to); | 690 size_t old_text_start = range.GetMin(); |
| 699 const string16 text = GetText().substr(old_text_start, | 691 const string16 text = GetText().substr(old_text_start, range.length()); |
| 700 std::abs(static_cast<long>(from - to))); | 692 bool backward = range.is_reversed(); |
| 701 bool backward = from > to; | |
| 702 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 693 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
| 703 bool delete_edit = AddOrMergeEditHistory(edit); | 694 bool delete_edit = AddOrMergeEditHistory(edit); |
| 704 edit->Redo(this); | 695 edit->Redo(this); |
| 705 if (delete_edit) | 696 if (delete_edit) |
| 706 delete edit; | 697 delete edit; |
| 707 } | 698 } |
| 708 | 699 |
| 709 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 700 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
| 710 MergeType merge_type, const string16& new_text) { | 701 MergeType merge_type, const string16& new_text) { |
| 711 size_t new_text_start = render_text_->MinOfSelection(); | 702 size_t new_text_start = render_text_->selection().GetMin(); |
| 712 size_t new_cursor_pos = new_text_start + new_text.length(); | 703 size_t new_cursor_pos = new_text_start + new_text.length(); |
| 713 ExecuteAndRecordReplace(merge_type, | 704 ExecuteAndRecordReplace(merge_type, |
| 714 GetCursorPosition(), | 705 GetCursorPosition(), |
| 715 new_cursor_pos, | 706 new_cursor_pos, |
| 716 new_text, | 707 new_text, |
| 717 new_text_start); | 708 new_text_start); |
| 718 } | 709 } |
| 719 | 710 |
| 720 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, | 711 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, |
| 721 size_t old_cursor_pos, | 712 size_t old_cursor_pos, |
| 722 size_t new_cursor_pos, | 713 size_t new_cursor_pos, |
| 723 const string16& new_text, | 714 const string16& new_text, |
| 724 size_t new_text_start) { | 715 size_t new_text_start) { |
| 725 size_t old_text_start = render_text_->MinOfSelection(); | 716 size_t old_text_start = render_text_->selection().GetMin(); |
| 726 bool backward = | 717 bool backward = render_text_->selection().is_reversed(); |
| 727 render_text_->GetSelectionStart() > render_text_->GetCursorPosition(); | |
| 728 Edit* edit = new ReplaceEdit(merge_type, | 718 Edit* edit = new ReplaceEdit(merge_type, |
| 729 GetSelectedText(), | 719 GetSelectedText(), |
| 730 old_cursor_pos, | 720 old_cursor_pos, |
| 731 old_text_start, | 721 old_text_start, |
| 732 backward, | 722 backward, |
| 733 new_cursor_pos, | 723 new_cursor_pos, |
| 734 new_text, | 724 new_text, |
| 735 new_text_start); | 725 new_text_start); |
| 736 bool delete_edit = AddOrMergeEditHistory(edit); | 726 bool delete_edit = AddOrMergeEditHistory(edit); |
| 737 edit->Redo(this); | 727 edit->Redo(this); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 if (delete_from != delete_to) | 770 if (delete_from != delete_to) |
| 781 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 771 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
| 782 if (!new_text.empty()) | 772 if (!new_text.empty()) |
| 783 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 773 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
| 784 render_text_->SetCursorPosition(new_cursor_pos); | 774 render_text_->SetCursorPosition(new_cursor_pos); |
| 785 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 775 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). |
| 786 // This looks fine feature and we may want to do the same. | 776 // This looks fine feature and we may want to do the same. |
| 787 } | 777 } |
| 788 | 778 |
| 789 } // namespace views | 779 } // namespace views |
| OLD | NEW |