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