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(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 update |
msw
2011/08/03 02:13:06
This is horrible, callers won't expect their argum
xji
2011/08/03 18:47:40
Agree that this is terrible. Previously the MoveCu
| |
387 // |selection| accordingly. | |
388 // TODO(xji): this is not ideal. But passing |selection| to | |
389 // ConfirmCompositionText() does not sounds right either. | |
390 selection->set_selection_start(render_text_->GetSelectionStart()); | |
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() - |
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 = ui::Range(render_text_->GetSelectionStart(), |
msw
2011/08/03 02:13:06
Optional: Change this to range->set_start() and ra
xji
2011/08/03 18:47:40
Done.
| |
414 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); | |
412 } | 423 } |
413 | 424 |
414 void TextfieldViewsModel::SelectAll() { | 425 void TextfieldViewsModel::SelectAll() { |
415 if (HasCompositionText()) | 426 if (HasCompositionText()) |
416 ConfirmCompositionText(); | 427 ConfirmCompositionText(); |
417 render_text_->SelectAll(); | 428 render_text_->SelectAll(); |
418 } | 429 } |
419 | 430 |
420 void TextfieldViewsModel::SelectWord() { | 431 void TextfieldViewsModel::SelectWord() { |
421 if (HasCompositionText()) | 432 if (HasCompositionText()) |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
485 | 496 |
486 bool TextfieldViewsModel::Cut() { | 497 bool TextfieldViewsModel::Cut() { |
487 if (!HasCompositionText() && HasSelection()) { | 498 if (!HasCompositionText() && HasSelection()) { |
488 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 499 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
489 ->GetClipboard()).WriteText(GetSelectedText()); | 500 ->GetClipboard()).WriteText(GetSelectedText()); |
490 // A trick to let undo/redo handle cursor correctly. | 501 // A trick to let undo/redo handle cursor correctly. |
491 // Undoing CUT moves the cursor to the end of the change rather | 502 // Undoing CUT moves the cursor to the end of the change rather |
492 // than beginning, unlike Delete/Backspace. | 503 // than beginning, unlike Delete/Backspace. |
493 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 504 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
494 // update DeleteEdit and remove this trick. | 505 // update DeleteEdit and remove this trick. |
495 ui::Range selection = render_text_->GetSelection(); | 506 gfx::SelectionModel sel(render_text_->GetCursorPosition(), |
496 render_text_->SetSelection(ui::Range(selection.end(), selection.start())); | 507 render_text_->GetSelectionStart(), |
508 render_text_->GetSelectionStart(), | |
509 gfx::SelectionModel::LEADING); | |
510 render_text_->SetSelectionModel(sel); | |
497 DeleteSelection(); | 511 DeleteSelection(); |
498 return true; | 512 return true; |
499 } | 513 } |
500 return false; | 514 return false; |
501 } | 515 } |
502 | 516 |
503 void TextfieldViewsModel::Copy() { | 517 void TextfieldViewsModel::Copy() { |
504 if (!HasCompositionText() && HasSelection()) { | 518 if (!HasCompositionText() && HasSelection()) { |
505 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 519 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
506 ->GetClipboard()).WriteText(GetSelectedText()); | 520 ->GetClipboard()).WriteText(GetSelectedText()); |
507 } | 521 } |
508 } | 522 } |
509 | 523 |
510 bool TextfieldViewsModel::Paste() { | 524 bool TextfieldViewsModel::Paste() { |
511 string16 result; | 525 string16 result; |
512 views::ViewsDelegate::views_delegate->GetClipboard() | 526 views::ViewsDelegate::views_delegate->GetClipboard() |
513 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); | 527 ->ReadText(ui::Clipboard::BUFFER_STANDARD, &result); |
514 if (!result.empty()) { | 528 if (!result.empty()) { |
515 InsertTextInternal(result, false); | 529 InsertTextInternal(result, false); |
516 return true; | 530 return true; |
517 } | 531 } |
518 return false; | 532 return false; |
519 } | 533 } |
520 | 534 |
521 bool TextfieldViewsModel::HasSelection() const { | 535 bool TextfieldViewsModel::HasSelection() const { |
522 return !render_text_->GetSelection().is_empty(); | 536 return !render_text_->EmptySelection(); |
523 } | 537 } |
524 | 538 |
525 void TextfieldViewsModel::DeleteSelection() { | 539 void TextfieldViewsModel::DeleteSelection() { |
526 DCHECK(!HasCompositionText()); | 540 DCHECK(!HasCompositionText()); |
527 DCHECK(HasSelection()); | 541 DCHECK(HasSelection()); |
528 ui::Range selection = render_text_->GetSelection(); | 542 ExecuteAndRecordDelete(render_text_->GetSelectionStart(), |
529 ExecuteAndRecordDelete(selection.start(), selection.end(), false); | 543 render_text_->GetCursorPosition(), false); |
530 } | 544 } |
531 | 545 |
532 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 546 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
533 const string16& text, size_t position) { | 547 const string16& text, size_t position) { |
534 if (HasCompositionText()) | 548 if (HasCompositionText()) |
535 CancelCompositionText(); | 549 CancelCompositionText(); |
536 ExecuteAndRecordReplace(DO_NOT_MERGE, | 550 ExecuteAndRecordReplace(DO_NOT_MERGE, |
537 GetCursorPosition(), | 551 GetCursorPosition(), |
538 position + text.length(), | 552 position + text.length(), |
539 text, | 553 text, |
(...skipping 20 matching lines...) Expand all Loading... | |
560 if (composition.text.empty()) | 574 if (composition.text.empty()) |
561 return; | 575 return; |
562 | 576 |
563 size_t cursor = GetCursorPosition(); | 577 size_t cursor = GetCursorPosition(); |
564 string16 new_text = GetText(); | 578 string16 new_text = GetText(); |
565 render_text_->SetText(new_text.insert(cursor, composition.text)); | 579 render_text_->SetText(new_text.insert(cursor, composition.text)); |
566 ui::Range range(cursor, cursor + composition.text.length()); | 580 ui::Range range(cursor, cursor + composition.text.length()); |
567 render_text_->SetCompositionRange(range); | 581 render_text_->SetCompositionRange(range); |
568 // TODO(msw): Support multiple composition underline ranges. | 582 // TODO(msw): Support multiple composition underline ranges. |
569 | 583 |
570 if (composition.selection.IsValid()) | 584 if (composition.selection.IsValid()) { |
571 render_text_->SetSelection(ui::Range( | 585 size_t start = std::min(range.start() + composition.selection.start(), |
572 std::min(range.start() + composition.selection.start(), range.end()), | 586 range.end()); |
573 std::min(range.start() + composition.selection.end(), range.end()))); | 587 size_t end = std::min(range.start() + composition.selection.end(), |
574 else | 588 range.end()); |
589 gfx::SelectionModel sel(start, end, end, | |
590 gfx::SelectionModel::PREVIOUS_GRAPHEME_TRAILING); | |
591 render_text_->SetSelectionModel(sel); | |
592 } else { | |
575 render_text_->SetCursorPosition(range.end()); | 593 render_text_->SetCursorPosition(range.end()); |
594 } | |
576 } | 595 } |
577 | 596 |
578 void TextfieldViewsModel::ConfirmCompositionText() { | 597 void TextfieldViewsModel::ConfirmCompositionText() { |
579 DCHECK(HasCompositionText()); | 598 DCHECK(HasCompositionText()); |
580 ui::Range range = render_text_->GetCompositionRange(); | 599 ui::Range range = render_text_->GetCompositionRange(); |
581 string16 text = GetText().substr(range.start(), range.length()); | 600 string16 text = GetText().substr(range.start(), range.length()); |
582 // TODO(oshima): current behavior on ChromeOS is a bit weird and not | 601 // 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. | 602 // sure exactly how this should work. Find out and fix if necessary. |
584 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); | 603 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
585 render_text_->SetCursorPosition(range.end()); | 604 render_text_->SetCursorPosition(range.end()); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
633 ExecuteAndRecordInsert(text, mergeable); | 652 ExecuteAndRecordInsert(text, mergeable); |
634 } | 653 } |
635 } | 654 } |
636 | 655 |
637 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, | 656 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, |
638 bool mergeable) { | 657 bool mergeable) { |
639 if (HasCompositionText()) { | 658 if (HasCompositionText()) { |
640 CancelCompositionText(); | 659 CancelCompositionText(); |
641 } else if (!HasSelection()) { | 660 } else if (!HasSelection()) { |
642 size_t cursor = GetCursorPosition(); | 661 size_t cursor = GetCursorPosition(); |
643 render_text_->SetSelection(ui::Range(cursor + text.length(), cursor)); | 662 gfx::SelectionModel sel(cursor + text.length(), cursor, |
msw
2011/08/03 02:13:06
Try this instead:
gfx::SelectionModel sel(render_t
xji
2011/08/03 18:47:40
Done and seems "sel.set_selection_end(cursor);" is
| |
663 render_text_->GetCursorBoundingCharIndex(), | |
664 render_text_->GetCursorPlacement()); | |
665 render_text_->SetSelectionModel(sel); | |
644 } | 666 } |
645 // Edit history is recorded in InsertText. | 667 // Edit history is recorded in InsertText. |
646 InsertTextInternal(text, mergeable); | 668 InsertTextInternal(text, mergeable); |
647 } | 669 } |
648 | 670 |
649 void TextfieldViewsModel::ClearEditHistory() { | 671 void TextfieldViewsModel::ClearEditHistory() { |
650 STLDeleteContainerPointers(edit_history_.begin(), | 672 STLDeleteContainerPointers(edit_history_.begin(), |
651 edit_history_.end()); | 673 edit_history_.end()); |
652 edit_history_.clear(); | 674 edit_history_.clear(); |
653 current_edit_ = edit_history_.end(); | 675 current_edit_ = edit_history_.end(); |
(...skipping 10 matching lines...) Expand all Loading... | |
664 delete_start++; | 686 delete_start++; |
665 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 687 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
666 edit_history_.erase(delete_start, edit_history_.end()); | 688 edit_history_.erase(delete_start, edit_history_.end()); |
667 } | 689 } |
668 | 690 |
669 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, | 691 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, |
670 size_t to, | 692 size_t to, |
671 bool mergeable) { | 693 bool mergeable) { |
672 size_t old_text_start = std::min(from, to); | 694 size_t old_text_start = std::min(from, to); |
673 const string16 text = GetText().substr(old_text_start, | 695 const string16 text = GetText().substr(old_text_start, |
674 std::abs(static_cast<long>(from - to))); | 696 std::abs(static_cast<long>(from - to))); |
675 bool backward = from > to; | 697 bool backward = from > to; |
676 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 698 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
677 bool delete_edit = AddOrMergeEditHistory(edit); | 699 bool delete_edit = AddOrMergeEditHistory(edit); |
678 edit->Redo(this); | 700 edit->Redo(this); |
679 if (delete_edit) | 701 if (delete_edit) |
680 delete edit; | 702 delete edit; |
681 } | 703 } |
682 | 704 |
683 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 705 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
684 MergeType merge_type, const string16& new_text) { | 706 MergeType merge_type, const string16& new_text) { |
685 size_t new_text_start = render_text_->GetSelection().GetMin(); | 707 size_t new_text_start = render_text_->MinOfSelection(); |
686 size_t new_cursor_pos = new_text_start + new_text.length(); | 708 size_t new_cursor_pos = new_text_start + new_text.length(); |
687 ExecuteAndRecordReplace(merge_type, | 709 ExecuteAndRecordReplace(merge_type, |
688 GetCursorPosition(), | 710 GetCursorPosition(), |
689 new_cursor_pos, | 711 new_cursor_pos, |
690 new_text, | 712 new_text, |
691 new_text_start); | 713 new_text_start); |
692 } | 714 } |
693 | 715 |
694 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, | 716 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, |
695 size_t old_cursor_pos, | 717 size_t old_cursor_pos, |
696 size_t new_cursor_pos, | 718 size_t new_cursor_pos, |
697 const string16& new_text, | 719 const string16& new_text, |
698 size_t new_text_start) { | 720 size_t new_text_start) { |
699 size_t old_text_start = render_text_->GetSelection().GetMin(); | 721 size_t old_text_start = render_text_->MinOfSelection(); |
700 bool backward = render_text_->GetSelection().is_reversed(); | 722 bool backward = render_text_->GetSelectionStart() > |
723 render_text_->GetCursorPosition(); | |
701 Edit* edit = new ReplaceEdit(merge_type, | 724 Edit* edit = new ReplaceEdit(merge_type, |
702 GetSelectedText(), | 725 GetSelectedText(), |
703 old_cursor_pos, | 726 old_cursor_pos, |
704 old_text_start, | 727 old_text_start, |
705 backward, | 728 backward, |
706 new_cursor_pos, | 729 new_cursor_pos, |
707 new_text, | 730 new_text, |
708 new_text_start); | 731 new_text_start); |
709 bool delete_edit = AddOrMergeEditHistory(edit); | 732 bool delete_edit = AddOrMergeEditHistory(edit); |
710 edit->Redo(this); | 733 edit->Redo(this); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
752 if (delete_from != delete_to) | 775 if (delete_from != delete_to) |
753 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 776 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
754 if (!new_text.empty()) | 777 if (!new_text.empty()) |
755 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 778 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
756 render_text_->SetCursorPosition(new_cursor_pos); | 779 render_text_->SetCursorPosition(new_cursor_pos); |
757 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 780 // 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. | 781 // This looks fine feature and we may want to do the same. |
759 } | 782 } |
760 | 783 |
761 } // namespace views | 784 } // namespace views |
OLD | NEW |