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 |