Chromium Code Reviews| 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 |