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 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 if (HasCompositionText()) | 383 if (HasCompositionText()) |
384 ConfirmCompositionText(); | 384 ConfirmCompositionText(); |
385 render_text_->MoveCursorRight(break_type, select); | 385 render_text_->MoveCursorRight(break_type, select); |
386 } | 386 } |
387 | 387 |
388 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { | 388 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& selection) { |
389 if (HasCompositionText()) { | 389 if (HasCompositionText()) { |
390 ConfirmCompositionText(); | 390 ConfirmCompositionText(); |
391 // ConfirmCompositionText() updates cursor position. Need to reflect it in | 391 // ConfirmCompositionText() updates cursor position. Need to reflect it in |
392 // the SelectionModel parameter of MoveCursorTo(). | 392 // the SelectionModel parameter of MoveCursorTo(). |
393 gfx::SelectionModel sel(selection); | 393 if (render_text_->GetSelectionStart() != selection.selection_end()) |
394 sel.set_selection_start(render_text_->GetSelectionStart()); | 394 return render_text_->SelectRange(ui::Range( |
| 395 render_text_->GetSelectionStart(), selection.selection_end())); |
| 396 gfx::SelectionModel sel(selection.selection_end(), |
| 397 selection.caret_pos(), |
| 398 selection.caret_placement()); |
395 return render_text_->MoveCursorTo(sel); | 399 return render_text_->MoveCursorTo(sel); |
396 } | 400 } |
397 return render_text_->MoveCursorTo(selection); | 401 return render_text_->MoveCursorTo(selection); |
398 } | 402 } |
399 | 403 |
400 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 404 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
401 if (HasCompositionText()) | 405 if (HasCompositionText()) |
402 ConfirmCompositionText(); | 406 ConfirmCompositionText(); |
403 return render_text_->MoveCursorTo(point, select); | 407 return render_text_->MoveCursorTo(point, select); |
404 } | 408 } |
405 | 409 |
406 string16 TextfieldViewsModel::GetSelectedText() const { | 410 string16 TextfieldViewsModel::GetSelectedText() const { |
407 return GetText().substr(render_text_->MinOfSelection(), | 411 return GetText().substr(render_text_->MinOfSelection(), |
408 (render_text_->MaxOfSelection() - render_text_->MinOfSelection())); | 412 (render_text_->MaxOfSelection() - render_text_->MinOfSelection())); |
409 } | 413 } |
410 | 414 |
| 415 void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const { |
| 416 range->set_start(render_text_->GetSelectionStart()); |
| 417 range->set_end(render_text_->GetCursorPosition()); |
| 418 } |
| 419 |
| 420 void TextfieldViewsModel::SelectRange(const ui::Range& range) { |
| 421 if (HasCompositionText()) |
| 422 ConfirmCompositionText(); |
| 423 render_text_->SelectRange(range); |
| 424 } |
| 425 |
411 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { | 426 void TextfieldViewsModel::GetSelectionModel(gfx::SelectionModel* sel) const { |
412 *sel = render_text_->selection_model(); | 427 *sel = render_text_->selection_model(); |
413 } | 428 } |
414 | 429 |
415 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { | 430 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { |
416 if (HasCompositionText()) | 431 if (HasCompositionText()) |
417 ConfirmCompositionText(); | 432 ConfirmCompositionText(); |
418 render_text_->MoveCursorTo(sel); | 433 render_text_->MoveCursorTo(sel); |
419 } | 434 } |
420 | 435 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 | 507 |
493 bool TextfieldViewsModel::Cut() { | 508 bool TextfieldViewsModel::Cut() { |
494 if (!HasCompositionText() && HasSelection()) { | 509 if (!HasCompositionText() && HasSelection()) { |
495 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 510 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
496 ->GetClipboard()).WriteText(GetSelectedText()); | 511 ->GetClipboard()).WriteText(GetSelectedText()); |
497 // A trick to let undo/redo handle cursor correctly. | 512 // A trick to let undo/redo handle cursor correctly. |
498 // Undoing CUT moves the cursor to the end of the change rather | 513 // Undoing CUT moves the cursor to the end of the change rather |
499 // than beginning, unlike Delete/Backspace. | 514 // than beginning, unlike Delete/Backspace. |
500 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 515 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
501 // update DeleteEdit and remove this trick. | 516 // update DeleteEdit and remove this trick. |
502 gfx::SelectionModel sel(render_text_->GetCursorPosition(), | 517 render_text_->SelectRange(ui::Range(render_text_->GetCursorPosition(), |
503 render_text_->GetSelectionStart(), | 518 render_text_->GetSelectionStart())); |
504 render_text_->GetSelectionStart(), | |
505 gfx::SelectionModel::LEADING); | |
506 render_text_->MoveCursorTo(sel); | |
507 DeleteSelection(); | 519 DeleteSelection(); |
508 return true; | 520 return true; |
509 } | 521 } |
510 return false; | 522 return false; |
511 } | 523 } |
512 | 524 |
513 void TextfieldViewsModel::Copy() { | 525 void TextfieldViewsModel::Copy() { |
514 if (!HasCompositionText() && HasSelection()) { | 526 if (!HasCompositionText() && HasSelection()) { |
515 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 527 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
516 ->GetClipboard()).WriteText(GetSelectedText()); | 528 ->GetClipboard()).WriteText(GetSelectedText()); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
575 render_text_->SetText(new_text.insert(cursor, composition.text)); | 587 render_text_->SetText(new_text.insert(cursor, composition.text)); |
576 ui::Range range(cursor, cursor + composition.text.length()); | 588 ui::Range range(cursor, cursor + composition.text.length()); |
577 render_text_->SetCompositionRange(range); | 589 render_text_->SetCompositionRange(range); |
578 // TODO(msw): Support multiple composition underline ranges. | 590 // TODO(msw): Support multiple composition underline ranges. |
579 | 591 |
580 if (composition.selection.IsValid()) { | 592 if (composition.selection.IsValid()) { |
581 size_t start = | 593 size_t start = |
582 std::min(range.start() + composition.selection.start(), range.end()); | 594 std::min(range.start() + composition.selection.start(), range.end()); |
583 size_t end = | 595 size_t end = |
584 std::min(range.start() + composition.selection.end(), range.end()); | 596 std::min(range.start() + composition.selection.end(), range.end()); |
585 gfx::SelectionModel sel(start, end); | 597 render_text_->SelectRange(ui::Range(start, end)); |
586 render_text_->MoveCursorTo(sel); | |
587 } else { | 598 } else { |
588 render_text_->SetCursorPosition(range.end()); | 599 render_text_->SetCursorPosition(range.end()); |
589 } | 600 } |
590 } | 601 } |
591 | 602 |
592 void TextfieldViewsModel::ConfirmCompositionText() { | 603 void TextfieldViewsModel::ConfirmCompositionText() { |
593 DCHECK(HasCompositionText()); | 604 DCHECK(HasCompositionText()); |
594 ui::Range range = render_text_->GetCompositionRange(); | 605 ui::Range range = render_text_->GetCompositionRange(); |
595 string16 text = GetText().substr(range.start(), range.length()); | 606 string16 text = GetText().substr(range.start(), range.length()); |
596 // 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 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
647 ExecuteAndRecordInsert(text, mergeable); | 658 ExecuteAndRecordInsert(text, mergeable); |
648 } | 659 } |
649 } | 660 } |
650 | 661 |
651 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, | 662 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, |
652 bool mergeable) { | 663 bool mergeable) { |
653 if (HasCompositionText()) { | 664 if (HasCompositionText()) { |
654 CancelCompositionText(); | 665 CancelCompositionText(); |
655 } else if (!HasSelection()) { | 666 } else if (!HasSelection()) { |
656 size_t cursor = GetCursorPosition(); | 667 size_t cursor = GetCursorPosition(); |
657 gfx::SelectionModel sel(render_text_->selection_model()); | 668 const gfx::SelectionModel& model = render_text_->selection_model(); |
658 sel.set_selection_start(render_text_->GetIndexOfNextGrapheme(cursor)); | 669 // When there is no selection, the default is to replace the next grapheme |
659 render_text_->MoveCursorTo(sel); | 670 // with |text|. So, need to find the index of next grapheme first. |
| 671 size_t next = render_text_->GetIndexOfNextGrapheme(cursor); |
| 672 if (next == model.selection_end()) |
| 673 render_text_->MoveCursorTo(model); |
| 674 else |
| 675 render_text_->SelectRange(ui::Range(next, model.selection_end())); |
660 } | 676 } |
661 // Edit history is recorded in InsertText. | 677 // Edit history is recorded in InsertText. |
662 InsertTextInternal(text, mergeable); | 678 InsertTextInternal(text, mergeable); |
663 } | 679 } |
664 | 680 |
665 void TextfieldViewsModel::ClearEditHistory() { | 681 void TextfieldViewsModel::ClearEditHistory() { |
666 STLDeleteContainerPointers(edit_history_.begin(), | 682 STLDeleteContainerPointers(edit_history_.begin(), |
667 edit_history_.end()); | 683 edit_history_.end()); |
668 edit_history_.clear(); | 684 edit_history_.clear(); |
669 current_edit_ = edit_history_.end(); | 685 current_edit_ = edit_history_.end(); |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
770 if (delete_from != delete_to) | 786 if (delete_from != delete_to) |
771 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 787 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
772 if (!new_text.empty()) | 788 if (!new_text.empty()) |
773 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 789 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
774 render_text_->SetCursorPosition(new_cursor_pos); | 790 render_text_->SetCursorPosition(new_cursor_pos); |
775 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 791 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). |
776 // This looks fine feature and we may want to do the same. | 792 // This looks fine feature and we may want to do the same. |
777 } | 793 } |
778 | 794 |
779 } // namespace views | 795 } // namespace views |
OLD | NEW |