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