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 23 matching lines...) Expand all Loading... | |
34 INSERT_EDIT, | 34 INSERT_EDIT, |
35 DELETE_EDIT, | 35 DELETE_EDIT, |
36 REPLACE_EDIT | 36 REPLACE_EDIT |
37 }; | 37 }; |
38 | 38 |
39 virtual ~Edit() { | 39 virtual ~Edit() { |
40 } | 40 } |
41 | 41 |
42 // Revert the change made by this edit in |model|. | 42 // Revert the change made by this edit in |model|. |
43 void Undo(TextfieldViewsModel* model) { | 43 void Undo(TextfieldViewsModel* model) { |
44 size_t old_cursor = delete_backward_ ? old_text_end() : old_text_start_; | |
45 model->ModifyText(new_text_start_, new_text_end(), | 44 model->ModifyText(new_text_start_, new_text_end(), |
46 old_text_, old_text_start_, | 45 old_text_, old_text_start_, |
47 old_cursor); | 46 old_cursor_pos_); |
48 } | 47 } |
49 | 48 |
50 // Apply the change of this edit to the |model|. | 49 // Apply the change of this edit to the |model|. |
51 void Redo(TextfieldViewsModel* model) { | 50 void Redo(TextfieldViewsModel* model) { |
52 model->ModifyText(old_text_start_, old_text_end(), | 51 model->ModifyText(old_text_start_, old_text_end(), |
53 new_text_, new_text_start_, | 52 new_text_, new_text_start_, |
54 new_text_end()); | 53 new_cursor_pos_); |
55 } | 54 } |
56 | 55 |
57 // Try to merge the edit into this edit. Returns true if merge was | 56 // Try to merge the |edit| into this edit. Returns true if merge was |
58 // successful, or false otherwise. Merged edit will be deleted after | 57 // successful, or false otherwise. Merged edit will be deleted after |
59 // redo and should not be reused. | 58 // redo and should not be reused. |
60 bool Merge(Edit* edit) { | 59 bool Merge(const Edit* edit) { |
61 return mergeable_ && edit->mergeable() && DoMerge(edit); | 60 if (edit->merge_with_previous()) { |
61 MergeSet(edit); | |
62 return true; | |
63 } | |
64 return mergeable() && edit->mergeable() && DoMerge(edit); | |
62 } | 65 } |
63 | 66 |
64 // Commits the edit and marks as un-mergeable. | 67 // Commits the edit and marks as un-mergeable. |
65 void Commit() { mergeable_ = false; } | 68 void Commit() { merge_type_ = DO_NOT_MERGE; } |
66 | 69 |
67 private: | 70 private: |
68 friend class InsertEdit; | 71 friend class InsertEdit; |
69 friend class ReplaceEdit; | 72 friend class ReplaceEdit; |
70 friend class DeleteEdit; | 73 friend class DeleteEdit; |
71 | 74 |
72 Edit(Type type, | 75 Edit(Type type, |
73 bool mergeable, | 76 MergeType merge_type, |
77 size_t old_cursor_pos, | |
74 string16 old_text, | 78 string16 old_text, |
75 size_t old_text_start, | 79 size_t old_text_start, |
76 bool delete_backward, | 80 bool delete_backward, |
81 size_t new_cursor_pos, | |
77 string16 new_text, | 82 string16 new_text, |
78 size_t new_text_start) | 83 size_t new_text_start) |
79 : type_(type), | 84 : type_(type), |
80 mergeable_(mergeable), | 85 merge_type_(merge_type), |
86 old_cursor_pos_(old_cursor_pos), | |
81 old_text_(old_text), | 87 old_text_(old_text), |
82 old_text_start_(old_text_start), | 88 old_text_start_(old_text_start), |
83 delete_backward_(delete_backward), | 89 delete_backward_(delete_backward), |
90 new_cursor_pos_(new_cursor_pos), | |
84 new_text_(new_text), | 91 new_text_(new_text), |
85 new_text_start_(new_text_start) { | 92 new_text_start_(new_text_start) { |
86 } | 93 } |
87 | 94 |
88 // A template method pattern that provides specific merge | 95 // A template method pattern that provides specific merge |
89 // implementation for each type of edit. | 96 // implementation for each type of edit. |
90 virtual bool DoMerge(Edit* edit) = 0; | 97 virtual bool DoMerge(const Edit* edit) = 0; |
91 | 98 |
92 Type type() { return type_; } | 99 Type type() const { return type_; } |
93 | 100 |
94 // Can this edit be merged? | 101 // Can this edit be merged? |
95 bool mergeable() { return mergeable_; } | 102 bool mergeable() const { return merge_type_ == MERGEABLE; } |
103 | |
104 // Does this edit should be merged with previous edit? | |
msw
2011/06/02 21:02:57
okay, the function is fine, but the comment needs
oshima
2011/06/03 00:39:24
Done.
| |
105 bool merge_with_previous() const { | |
106 return merge_type_ == MERGE_WITH_PREVIOUS; | |
107 } | |
96 | 108 |
97 // Returns the end index of the |old_text_|. | 109 // Returns the end index of the |old_text_|. |
98 size_t old_text_end() { return old_text_start_ + old_text_.length(); } | 110 size_t old_text_end() const { return old_text_start_ + old_text_.length(); } |
99 | 111 |
100 // Returns the end index of the |new_text_|. | 112 // Returns the end index of the |new_text_|. |
101 size_t new_text_end() { return new_text_start_ + new_text_.length(); } | 113 size_t new_text_end() const { return new_text_start_ + new_text_.length(); } |
114 | |
115 // Merge the Set edit into the current edit. This is a special case to | |
116 // handle an omnibox setting autocomplete string after new character is | |
117 // typed in. | |
118 void MergeSet(const Edit* edit) { | |
119 CHECK_EQ(REPLACE_EDIT, edit->type_); | |
120 CHECK_EQ(0U, edit->old_text_start_); | |
121 CHECK_EQ(0U, edit->new_text_start_); | |
122 string16 old_text = edit->old_text_; | |
123 old_text.erase(new_text_start_, new_text_.length()); | |
124 old_text.insert(old_text_start_, old_text_); | |
125 // Set edit replaces entire text, so remember that. | |
msw
2011/06/02 21:02:57
I ran through an example just now and it makes sen
oshima
2011/06/03 00:39:24
Done.
| |
126 old_text_ = old_text; | |
127 old_text_start_ = edit->old_text_start_; | |
128 delete_backward_ = false; | |
129 | |
130 new_text_ = edit->new_text_; | |
131 new_text_start_ = edit->new_text_start_; | |
132 merge_type_ = DO_NOT_MERGE; | |
133 } | |
102 | 134 |
103 Type type_; | 135 Type type_; |
104 | 136 |
105 // True if the edit can be marged. | 137 // True if the edit can be marged. |
106 bool mergeable_; | 138 MergeType merge_type_; |
139 // Old cursor position. | |
140 size_t old_cursor_pos_; | |
107 // Deleted text by this edit. | 141 // Deleted text by this edit. |
108 string16 old_text_; | 142 string16 old_text_; |
109 // The index of |old_text_|. | 143 // The index of |old_text_|. |
110 size_t old_text_start_; | 144 size_t old_text_start_; |
111 // True if the deletion is made backward. | 145 // True if the deletion is made backward. |
112 bool delete_backward_; | 146 bool delete_backward_; |
147 // New cursor position. | |
148 size_t new_cursor_pos_; | |
113 // Added text. | 149 // Added text. |
114 string16 new_text_; | 150 string16 new_text_; |
115 // The index of |new_text_| | 151 // The index of |new_text_| |
116 size_t new_text_start_; | 152 size_t new_text_start_; |
117 | 153 |
118 DISALLOW_COPY_AND_ASSIGN(Edit); | 154 DISALLOW_COPY_AND_ASSIGN(Edit); |
119 }; | 155 }; |
120 | 156 |
121 class InsertEdit : public Edit { | 157 class InsertEdit : public Edit { |
122 public: | 158 public: |
123 InsertEdit(bool mergeable, const string16& new_text, size_t at) | 159 InsertEdit(bool mergeable, const string16& new_text, size_t at) |
124 : Edit(INSERT_EDIT, mergeable, string16(), at, false, new_text, at) { | 160 : Edit(INSERT_EDIT, |
161 mergeable ? MERGEABLE : DO_NOT_MERGE, | |
162 at /* old cursor */, | |
163 string16(), | |
164 at, | |
165 false /* N/A */, | |
166 at + new_text.length() /* new cursor */, | |
167 new_text, | |
168 at) { | |
125 } | 169 } |
126 | 170 |
127 // Edit implementation. | 171 // Edit implementation. |
128 virtual bool DoMerge(Edit* edit) OVERRIDE { | 172 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
129 if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_) | 173 if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_) |
130 return false; | 174 return false; |
131 // If continuous edit, merge it. | 175 // If continuous edit, merge it. |
132 // TODO(oshima): gtk splits edits between whitespace. Find out what | 176 // TODO(oshima): gtk splits edits between whitespace. Find out what |
133 // we want to here and implement if necessary. | 177 // we want to here and implement if necessary. |
134 new_text_ += edit->new_text_; | 178 new_text_ += edit->new_text_; |
179 new_cursor_pos_ = edit->new_cursor_pos_; | |
135 return true; | 180 return true; |
136 } | 181 } |
137 }; | 182 }; |
138 | 183 |
139 class ReplaceEdit : public Edit { | 184 class ReplaceEdit : public Edit { |
140 public: | 185 public: |
141 ReplaceEdit(bool mergeable, | 186 ReplaceEdit(MergeType merge_type, |
142 const string16& old_text, | 187 const string16& old_text, |
188 size_t old_cursor_pos, | |
143 size_t old_text_start, | 189 size_t old_text_start, |
144 bool backward, | 190 bool backward, |
191 size_t new_cursor_pos, | |
145 const string16& new_text, | 192 const string16& new_text, |
146 size_t new_text_start) | 193 size_t new_text_start) |
147 : Edit(REPLACE_EDIT, mergeable, | 194 : Edit(REPLACE_EDIT, merge_type, |
195 old_cursor_pos, | |
148 old_text, | 196 old_text, |
149 old_text_start, | 197 old_text_start, |
150 backward, | 198 backward, |
199 new_cursor_pos, | |
151 new_text, | 200 new_text, |
152 new_text_start) { | 201 new_text_start) { |
153 } | 202 } |
154 | 203 |
155 // Edit implementation. | 204 // Edit implementation. |
156 virtual bool DoMerge(Edit* edit) OVERRIDE { | 205 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
157 if (edit->type() == DELETE_EDIT || | 206 if (edit->type() == DELETE_EDIT || |
158 new_text_end() != edit->old_text_start_ || | 207 new_text_end() != edit->old_text_start_ || |
159 edit->old_text_start_ != edit->new_text_start_) | 208 edit->old_text_start_ != edit->new_text_start_) |
160 return false; | 209 return false; |
161 old_text_ += edit->old_text_; | 210 old_text_ += edit->old_text_; |
162 new_text_ += edit->new_text_; | 211 new_text_ += edit->new_text_; |
212 new_cursor_pos_ = edit->new_cursor_pos_; | |
163 return true; | 213 return true; |
164 } | 214 } |
165 }; | 215 }; |
166 | 216 |
167 class DeleteEdit : public Edit { | 217 class DeleteEdit : public Edit { |
168 public: | 218 public: |
169 DeleteEdit(bool mergeable, | 219 DeleteEdit(bool mergeable, |
170 const string16& text, | 220 const string16& text, |
171 size_t text_start, | 221 size_t text_start, |
172 bool backward) | 222 bool backward) |
173 : Edit(DELETE_EDIT, mergeable, | 223 : Edit(DELETE_EDIT, |
224 mergeable ? MERGEABLE : DO_NOT_MERGE, | |
225 (backward ? text_start + text.length() : text_start), | |
174 text, | 226 text, |
175 text_start, | 227 text_start, |
176 backward, | 228 backward, |
229 text_start, | |
177 string16(), | 230 string16(), |
178 text_start) { | 231 text_start) { |
179 } | 232 } |
180 | 233 |
181 // Edit implementation. | 234 // Edit implementation. |
182 virtual bool DoMerge(Edit* edit) OVERRIDE { | 235 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
183 if (edit->type() != DELETE_EDIT) | 236 if (edit->type() != DELETE_EDIT) |
184 return false; | 237 return false; |
185 | 238 |
186 if (delete_backward_) { | 239 if (delete_backward_) { |
187 // backspace can be merged only with backspace at the | 240 // backspace can be merged only with backspace at the |
188 // same position. | 241 // same position. |
189 if (!edit->delete_backward_ || old_text_start_ != edit->old_text_end()) | 242 if (!edit->delete_backward_ || old_text_start_ != edit->old_text_end()) |
190 return false; | 243 return false; |
191 old_text_start_ = edit->old_text_start_; | 244 old_text_start_ = edit->old_text_start_; |
192 old_text_ = edit->old_text_ + old_text_; | 245 old_text_ = edit->old_text_ + old_text_; |
246 new_cursor_pos_ = edit->new_cursor_pos_; | |
193 } else { | 247 } else { |
194 // delete can be merged only with delete at the same | 248 // delete can be merged only with delete at the same |
195 // position. | 249 // position. |
196 if (edit->delete_backward_ || old_text_start_ != edit->old_text_start_) | 250 if (edit->delete_backward_ || old_text_start_ != edit->old_text_start_) |
197 return false; | 251 return false; |
198 old_text_ += edit->old_text_; | 252 old_text_ += edit->old_text_; |
199 } | 253 } |
200 return true; | 254 return true; |
201 } | 255 } |
202 }; | 256 }; |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
306 DCHECK(CheckInvariant(style_ranges)); | 360 DCHECK(CheckInvariant(style_ranges)); |
307 #endif | 361 #endif |
308 } | 362 } |
309 | 363 |
310 } // namespace | 364 } // namespace |
311 | 365 |
312 using internal::Edit; | 366 using internal::Edit; |
313 using internal::DeleteEdit; | 367 using internal::DeleteEdit; |
314 using internal::InsertEdit; | 368 using internal::InsertEdit; |
315 using internal::ReplaceEdit; | 369 using internal::ReplaceEdit; |
370 using internal::MergeType; | |
371 using internal::DO_NOT_MERGE; | |
372 using internal::MERGE_WITH_PREVIOUS; | |
373 using internal::MERGEABLE; | |
316 | 374 |
317 ///////////////////////////////////////////////////////////////// | 375 ///////////////////////////////////////////////////////////////// |
318 // TextfieldViewsModel: public | 376 // TextfieldViewsModel: public |
319 | 377 |
320 TextfieldViewsModel::Delegate::~Delegate() { | 378 TextfieldViewsModel::Delegate::~Delegate() { |
321 } | 379 } |
322 | 380 |
323 TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate) | 381 TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate) |
324 : delegate_(delegate), | 382 : delegate_(delegate), |
325 cursor_pos_(0), | 383 cursor_pos_(0), |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
388 fragments->push_back(TextFragment(current, end, kNormalStyle)); | 446 fragments->push_back(TextFragment(current, end, kNormalStyle)); |
389 } | 447 } |
390 | 448 |
391 bool TextfieldViewsModel::SetText(const string16& text) { | 449 bool TextfieldViewsModel::SetText(const string16& text) { |
392 bool changed = false; | 450 bool changed = false; |
393 if (HasCompositionText()) { | 451 if (HasCompositionText()) { |
394 ConfirmCompositionText(); | 452 ConfirmCompositionText(); |
395 changed = true; | 453 changed = true; |
396 } | 454 } |
397 if (text_ != text) { | 455 if (text_ != text) { |
398 if (changed) // no need to remember composition. | 456 if (changed) // No need to remember composition. |
399 Undo(); | 457 Undo(); |
458 size_t old_cursor = cursor_pos_; | |
459 size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor; | |
400 SelectAll(); | 460 SelectAll(); |
401 InsertTextInternal(text, false); | 461 // If there is a composition text, don't merge with previous edit. |
402 cursor_pos_ = 0; | 462 // Otherwise, force merge the edits. |
463 ExecuteAndRecordReplace( | |
464 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, | |
465 old_cursor, | |
466 new_cursor, | |
467 text, | |
468 0U); | |
469 cursor_pos_ = new_cursor; | |
403 } | 470 } |
404 ClearSelection(); | 471 ClearSelection(); |
405 return changed; | 472 return changed; |
406 } | 473 } |
407 | 474 |
408 void TextfieldViewsModel::Append(const string16& text) { | 475 void TextfieldViewsModel::Append(const string16& text) { |
409 if (HasCompositionText()) | 476 if (HasCompositionText()) |
410 ConfirmCompositionText(); | 477 ConfirmCompositionText(); |
411 size_t save = cursor_pos_; | 478 size_t save = cursor_pos_; |
412 MoveCursorToEnd(false); | 479 MoveCursorToEnd(false); |
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
660 } | 727 } |
661 | 728 |
662 bool TextfieldViewsModel::Undo() { | 729 bool TextfieldViewsModel::Undo() { |
663 if (!CanUndo()) | 730 if (!CanUndo()) |
664 return false; | 731 return false; |
665 DCHECK(!HasCompositionText()); | 732 DCHECK(!HasCompositionText()); |
666 if (HasCompositionText()) // safe guard for release build. | 733 if (HasCompositionText()) // safe guard for release build. |
667 CancelCompositionText(); | 734 CancelCompositionText(); |
668 | 735 |
669 string16 old = text_; | 736 string16 old = text_; |
737 size_t old_cursor = cursor_pos_; | |
670 (*current_edit_)->Commit(); | 738 (*current_edit_)->Commit(); |
671 (*current_edit_)->Undo(this); | 739 (*current_edit_)->Undo(this); |
672 | 740 |
673 if (current_edit_ == edit_history_.begin()) | 741 if (current_edit_ == edit_history_.begin()) |
674 current_edit_ = edit_history_.end(); | 742 current_edit_ = edit_history_.end(); |
675 else | 743 else |
676 current_edit_--; | 744 current_edit_--; |
677 return old != text_; | 745 return old != text_ || old_cursor != cursor_pos_; |
678 } | 746 } |
679 | 747 |
680 bool TextfieldViewsModel::Redo() { | 748 bool TextfieldViewsModel::Redo() { |
681 if (!CanRedo()) | 749 if (!CanRedo()) |
682 return false; | 750 return false; |
683 DCHECK(!HasCompositionText()); | 751 DCHECK(!HasCompositionText()); |
684 if (HasCompositionText()) // safe guard for release build. | 752 if (HasCompositionText()) // safe guard for release build. |
685 CancelCompositionText(); | 753 CancelCompositionText(); |
686 | 754 |
687 if (current_edit_ == edit_history_.end()) | 755 if (current_edit_ == edit_history_.end()) |
688 current_edit_ = edit_history_.begin(); | 756 current_edit_ = edit_history_.begin(); |
689 else | 757 else |
690 current_edit_ ++; | 758 current_edit_ ++; |
691 string16 old = text_; | 759 string16 old = text_; |
760 size_t old_cursor = cursor_pos_; | |
692 (*current_edit_)->Redo(this); | 761 (*current_edit_)->Redo(this); |
693 return old != text_; | 762 return old != text_ || old_cursor != cursor_pos_; |
694 } | 763 } |
695 | 764 |
696 bool TextfieldViewsModel::Cut() { | 765 bool TextfieldViewsModel::Cut() { |
697 if (!HasCompositionText() && HasSelection()) { | 766 if (!HasCompositionText() && HasSelection()) { |
698 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 767 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
699 ->GetClipboard()).WriteText(GetSelectedText()); | 768 ->GetClipboard()).WriteText(GetSelectedText()); |
769 // A trick to let undo/redo handle cursor correctly. | |
770 // Undoing CUT moves the cursor to the end of the change rather | |
771 // than beginning, unlike Delete/Backspace. | |
772 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | |
773 // update DeleteEdit and remove this trick. | |
774 std::swap(cursor_pos_, selection_start_); | |
700 DeleteSelection(); | 775 DeleteSelection(); |
701 return true; | 776 return true; |
702 } | 777 } |
703 return false; | 778 return false; |
704 } | 779 } |
705 | 780 |
706 void TextfieldViewsModel::Copy() { | 781 void TextfieldViewsModel::Copy() { |
707 if (!HasCompositionText() && HasSelection()) { | 782 if (!HasCompositionText() && HasSelection()) { |
708 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate | 783 ui::ScopedClipboardWriter(views::ViewsDelegate::views_delegate |
709 ->GetClipboard()).WriteText(GetSelectedText()); | 784 ->GetClipboard()).WriteText(GetSelectedText()); |
(...skipping 18 matching lines...) Expand all Loading... | |
728 void TextfieldViewsModel::DeleteSelection() { | 803 void TextfieldViewsModel::DeleteSelection() { |
729 DCHECK(!HasCompositionText()); | 804 DCHECK(!HasCompositionText()); |
730 DCHECK(HasSelection()); | 805 DCHECK(HasSelection()); |
731 ExecuteAndRecordDelete(selection_start_, cursor_pos_, false); | 806 ExecuteAndRecordDelete(selection_start_, cursor_pos_, false); |
732 } | 807 } |
733 | 808 |
734 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 809 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
735 const string16& text, size_t position) { | 810 const string16& text, size_t position) { |
736 if (HasCompositionText()) | 811 if (HasCompositionText()) |
737 CancelCompositionText(); | 812 CancelCompositionText(); |
738 ExecuteAndRecordReplaceAt(text, position, false); | 813 ExecuteAndRecordReplace(DO_NOT_MERGE, |
814 cursor_pos_, | |
815 position + text.length(), | |
816 text, | |
817 position); | |
739 } | 818 } |
740 | 819 |
741 string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const { | 820 string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const { |
742 if (range.IsValid() && range.GetMin() < text_.length()) | 821 if (range.IsValid() && range.GetMin() < text_.length()) |
743 return text_.substr(range.GetMin(), range.length()); | 822 return text_.substr(range.GetMin(), range.length()); |
744 return string16(); | 823 return string16(); |
745 } | 824 } |
746 | 825 |
747 void TextfieldViewsModel::GetTextRange(ui::Range* range) const { | 826 void TextfieldViewsModel::GetTextRange(ui::Range* range) const { |
748 *range = ui::Range(0, text_.length()); | 827 *range = ui::Range(0, text_.length()); |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
872 } | 951 } |
873 return position; | 952 return position; |
874 } | 953 } |
875 | 954 |
876 void TextfieldViewsModel::InsertTextInternal(const string16& text, | 955 void TextfieldViewsModel::InsertTextInternal(const string16& text, |
877 bool mergeable) { | 956 bool mergeable) { |
878 if (HasCompositionText()) { | 957 if (HasCompositionText()) { |
879 CancelCompositionText(); | 958 CancelCompositionText(); |
880 ExecuteAndRecordInsert(text, mergeable); | 959 ExecuteAndRecordInsert(text, mergeable); |
881 } else if (HasSelection()) { | 960 } else if (HasSelection()) { |
882 ExecuteAndRecordReplace(text, mergeable); | 961 ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, |
962 text); | |
883 } else { | 963 } else { |
884 ExecuteAndRecordInsert(text, mergeable); | 964 ExecuteAndRecordInsert(text, mergeable); |
885 } | 965 } |
886 } | 966 } |
887 | 967 |
888 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, | 968 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, |
889 bool mergeable) { | 969 bool mergeable) { |
890 if (HasCompositionText()) | 970 if (HasCompositionText()) |
891 CancelCompositionText(); | 971 CancelCompositionText(); |
892 else if (!HasSelection()) | 972 else if (!HasSelection()) |
(...skipping 11 matching lines...) Expand all Loading... | |
904 | 984 |
905 void TextfieldViewsModel::ClearRedoHistory() { | 985 void TextfieldViewsModel::ClearRedoHistory() { |
906 if (edit_history_.begin() == edit_history_.end()) | 986 if (edit_history_.begin() == edit_history_.end()) |
907 return; | 987 return; |
908 if (current_edit_ == edit_history_.end()) { | 988 if (current_edit_ == edit_history_.end()) { |
909 ClearEditHistory(); | 989 ClearEditHistory(); |
910 return; | 990 return; |
911 } | 991 } |
912 EditHistory::iterator delete_start = current_edit_; | 992 EditHistory::iterator delete_start = current_edit_; |
913 delete_start++; | 993 delete_start++; |
914 STLDeleteContainerPointers(delete_start, | 994 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
915 edit_history_.end()); | |
916 edit_history_.erase(delete_start, edit_history_.end()); | 995 edit_history_.erase(delete_start, edit_history_.end()); |
917 } | 996 } |
918 | 997 |
919 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, | 998 void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from, |
920 size_t to, | 999 size_t to, |
921 bool mergeable) { | 1000 bool mergeable) { |
922 size_t old_text_start = std::min(from, to); | 1001 size_t old_text_start = std::min(from, to); |
923 const string16 text = text_.substr(old_text_start, | 1002 const string16 text = text_.substr(old_text_start, |
924 std::abs(static_cast<long>(from - to))); | 1003 std::abs(static_cast<long>(from - to))); |
925 Edit* edit = new DeleteEdit(mergeable, | 1004 bool backward = from > to; |
926 text, | 1005 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
927 old_text_start, | |
928 (from > to)); | |
929 bool delete_edit = AddOrMergeEditHistory(edit); | 1006 bool delete_edit = AddOrMergeEditHistory(edit); |
930 edit->Redo(this); | 1007 edit->Redo(this); |
931 if (delete_edit) | 1008 if (delete_edit) |
932 delete edit; | 1009 delete edit; |
933 } | 1010 } |
934 | 1011 |
935 void TextfieldViewsModel::ExecuteAndRecordReplace(const string16& text, | 1012 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
936 bool mergeable) { | 1013 MergeType merge_type, const string16& new_text) { |
937 size_t at = std::min(cursor_pos_, selection_start_); | 1014 size_t new_text_start = std::min(cursor_pos_, selection_start_); |
938 ExecuteAndRecordReplaceAt(text, at, mergeable); | 1015 size_t new_cursor_pos = new_text_start + new_text.length(); |
1016 ExecuteAndRecordReplace(merge_type, | |
1017 cursor_pos_, | |
1018 new_cursor_pos, | |
1019 new_text, | |
1020 new_text_start); | |
939 } | 1021 } |
940 | 1022 |
941 void TextfieldViewsModel::ExecuteAndRecordReplaceAt(const string16& text, | 1023 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, |
942 size_t at, | 1024 size_t old_cursor_pos, |
943 bool mergeable) { | 1025 size_t new_cursor_pos, |
944 size_t text_start = std::min(cursor_pos_, selection_start_); | 1026 const string16& new_text, |
945 Edit* edit = new ReplaceEdit(mergeable, | 1027 size_t new_text_start) { |
1028 size_t old_text_start = std::min(cursor_pos_, selection_start_); | |
1029 bool backward = selection_start_ > cursor_pos_; | |
1030 Edit* edit = new ReplaceEdit(merge_type, | |
946 GetSelectedText(), | 1031 GetSelectedText(), |
947 text_start, | 1032 old_cursor_pos, |
948 selection_start_ > cursor_pos_, | 1033 old_text_start, |
949 text, | 1034 backward, |
950 at); | 1035 new_cursor_pos, |
1036 new_text, | |
1037 new_text_start); | |
951 bool delete_edit = AddOrMergeEditHistory(edit); | 1038 bool delete_edit = AddOrMergeEditHistory(edit); |
952 edit->Redo(this); | 1039 edit->Redo(this); |
953 if (delete_edit) | 1040 if (delete_edit) |
954 delete edit; | 1041 delete edit; |
955 } | 1042 } |
956 | 1043 |
957 void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text, | 1044 void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text, |
958 bool mergeable) { | 1045 bool mergeable) { |
959 Edit* edit = new InsertEdit(mergeable, text, cursor_pos_); | 1046 Edit* edit = new InsertEdit(mergeable, text, cursor_pos_); |
960 bool delete_edit = AddOrMergeEditHistory(edit); | 1047 bool delete_edit = AddOrMergeEditHistory(edit); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1001 } | 1088 } |
1002 | 1089 |
1003 // static | 1090 // static |
1004 TextStyle* TextfieldViewsModel::CreateUnderlineStyle() { | 1091 TextStyle* TextfieldViewsModel::CreateUnderlineStyle() { |
1005 TextStyle* style = new TextStyle(); | 1092 TextStyle* style = new TextStyle(); |
1006 style->set_underline(true); | 1093 style->set_underline(true); |
1007 return style; | 1094 return style; |
1008 } | 1095 } |
1009 | 1096 |
1010 } // namespace views | 1097 } // namespace views |
OLD | NEW |