| 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 void Commit() { merge_type_ = DO_NOT_MERGE; } | 74 void Commit() { merge_type_ = DO_NOT_MERGE; } |
| 75 | 75 |
| 76 private: | 76 private: |
| 77 friend class InsertEdit; | 77 friend class InsertEdit; |
| 78 friend class ReplaceEdit; | 78 friend class ReplaceEdit; |
| 79 friend class DeleteEdit; | 79 friend class DeleteEdit; |
| 80 | 80 |
| 81 Edit(Type type, | 81 Edit(Type type, |
| 82 MergeType merge_type, | 82 MergeType merge_type, |
| 83 size_t old_cursor_pos, | 83 size_t old_cursor_pos, |
| 84 const string16& old_text, | 84 const base::string16& old_text, |
| 85 size_t old_text_start, | 85 size_t old_text_start, |
| 86 bool delete_backward, | 86 bool delete_backward, |
| 87 size_t new_cursor_pos, | 87 size_t new_cursor_pos, |
| 88 const string16& new_text, | 88 const base::string16& new_text, |
| 89 size_t new_text_start) | 89 size_t new_text_start) |
| 90 : type_(type), | 90 : type_(type), |
| 91 merge_type_(merge_type), | 91 merge_type_(merge_type), |
| 92 old_cursor_pos_(old_cursor_pos), | 92 old_cursor_pos_(old_cursor_pos), |
| 93 old_text_(old_text), | 93 old_text_(old_text), |
| 94 old_text_start_(old_text_start), | 94 old_text_start_(old_text_start), |
| 95 delete_backward_(delete_backward), | 95 delete_backward_(delete_backward), |
| 96 new_cursor_pos_(new_cursor_pos), | 96 new_cursor_pos_(new_cursor_pos), |
| 97 new_text_(new_text), | 97 new_text_(new_text), |
| 98 new_text_start_(new_text_start) { | 98 new_text_start_(new_text_start) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 118 // Returns the end index of the |new_text_|. | 118 // Returns the end index of the |new_text_|. |
| 119 size_t new_text_end() const { return new_text_start_ + new_text_.length(); } | 119 size_t new_text_end() const { return new_text_start_ + new_text_.length(); } |
| 120 | 120 |
| 121 // Merge the replace edit into the current edit. This is a special case to | 121 // Merge the replace edit into the current edit. This is a special case to |
| 122 // handle an omnibox setting autocomplete string after new character is | 122 // handle an omnibox setting autocomplete string after new character is |
| 123 // typed in. | 123 // typed in. |
| 124 void MergeReplace(const Edit* edit) { | 124 void MergeReplace(const Edit* edit) { |
| 125 CHECK_EQ(REPLACE_EDIT, edit->type_); | 125 CHECK_EQ(REPLACE_EDIT, edit->type_); |
| 126 CHECK_EQ(0U, edit->old_text_start_); | 126 CHECK_EQ(0U, edit->old_text_start_); |
| 127 CHECK_EQ(0U, edit->new_text_start_); | 127 CHECK_EQ(0U, edit->new_text_start_); |
| 128 string16 old_text = edit->old_text_; | 128 base::string16 old_text = edit->old_text_; |
| 129 old_text.erase(new_text_start_, new_text_.length()); | 129 old_text.erase(new_text_start_, new_text_.length()); |
| 130 old_text.insert(old_text_start_, old_text_); | 130 old_text.insert(old_text_start_, old_text_); |
| 131 // SetText() replaces entire text. Set |old_text_| to the entire | 131 // SetText() replaces entire text. Set |old_text_| to the entire |
| 132 // replaced text with |this| edit undone. | 132 // replaced text with |this| edit undone. |
| 133 old_text_ = old_text; | 133 old_text_ = old_text; |
| 134 old_text_start_ = edit->old_text_start_; | 134 old_text_start_ = edit->old_text_start_; |
| 135 delete_backward_ = false; | 135 delete_backward_ = false; |
| 136 | 136 |
| 137 new_text_ = edit->new_text_; | 137 new_text_ = edit->new_text_; |
| 138 new_text_start_ = edit->new_text_start_; | 138 new_text_start_ = edit->new_text_start_; |
| 139 merge_type_ = DO_NOT_MERGE; | 139 merge_type_ = DO_NOT_MERGE; |
| 140 } | 140 } |
| 141 | 141 |
| 142 Type type_; | 142 Type type_; |
| 143 | 143 |
| 144 // True if the edit can be marged. | 144 // True if the edit can be marged. |
| 145 MergeType merge_type_; | 145 MergeType merge_type_; |
| 146 // Old cursor position. | 146 // Old cursor position. |
| 147 size_t old_cursor_pos_; | 147 size_t old_cursor_pos_; |
| 148 // Deleted text by this edit. | 148 // Deleted text by this edit. |
| 149 string16 old_text_; | 149 base::string16 old_text_; |
| 150 // The index of |old_text_|. | 150 // The index of |old_text_|. |
| 151 size_t old_text_start_; | 151 size_t old_text_start_; |
| 152 // True if the deletion is made backward. | 152 // True if the deletion is made backward. |
| 153 bool delete_backward_; | 153 bool delete_backward_; |
| 154 // New cursor position. | 154 // New cursor position. |
| 155 size_t new_cursor_pos_; | 155 size_t new_cursor_pos_; |
| 156 // Added text. | 156 // Added text. |
| 157 string16 new_text_; | 157 base::string16 new_text_; |
| 158 // The index of |new_text_| | 158 // The index of |new_text_| |
| 159 size_t new_text_start_; | 159 size_t new_text_start_; |
| 160 | 160 |
| 161 DISALLOW_COPY_AND_ASSIGN(Edit); | 161 DISALLOW_COPY_AND_ASSIGN(Edit); |
| 162 }; | 162 }; |
| 163 | 163 |
| 164 class InsertEdit : public Edit { | 164 class InsertEdit : public Edit { |
| 165 public: | 165 public: |
| 166 InsertEdit(bool mergeable, const string16& new_text, size_t at) | 166 InsertEdit(bool mergeable, const base::string16& new_text, size_t at) |
| 167 : Edit(INSERT_EDIT, | 167 : Edit(INSERT_EDIT, |
| 168 mergeable ? MERGEABLE : DO_NOT_MERGE, | 168 mergeable ? MERGEABLE : DO_NOT_MERGE, |
| 169 at /* old cursor */, | 169 at /* old cursor */, |
| 170 string16(), | 170 base::string16(), |
| 171 at, | 171 at, |
| 172 false /* N/A */, | 172 false /* N/A */, |
| 173 at + new_text.length() /* new cursor */, | 173 at + new_text.length() /* new cursor */, |
| 174 new_text, | 174 new_text, |
| 175 at) { | 175 at) { |
| 176 } | 176 } |
| 177 | 177 |
| 178 // Edit implementation. | 178 // Edit implementation. |
| 179 virtual bool DoMerge(const Edit* edit) OVERRIDE { | 179 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
| 180 if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_) | 180 if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_) |
| 181 return false; | 181 return false; |
| 182 // If continuous edit, merge it. | 182 // If continuous edit, merge it. |
| 183 // TODO(oshima): gtk splits edits between whitespace. Find out what | 183 // TODO(oshima): gtk splits edits between whitespace. Find out what |
| 184 // we want to here and implement if necessary. | 184 // we want to here and implement if necessary. |
| 185 new_text_ += edit->new_text_; | 185 new_text_ += edit->new_text_; |
| 186 new_cursor_pos_ = edit->new_cursor_pos_; | 186 new_cursor_pos_ = edit->new_cursor_pos_; |
| 187 return true; | 187 return true; |
| 188 } | 188 } |
| 189 }; | 189 }; |
| 190 | 190 |
| 191 class ReplaceEdit : public Edit { | 191 class ReplaceEdit : public Edit { |
| 192 public: | 192 public: |
| 193 ReplaceEdit(MergeType merge_type, | 193 ReplaceEdit(MergeType merge_type, |
| 194 const string16& old_text, | 194 const base::string16& old_text, |
| 195 size_t old_cursor_pos, | 195 size_t old_cursor_pos, |
| 196 size_t old_text_start, | 196 size_t old_text_start, |
| 197 bool backward, | 197 bool backward, |
| 198 size_t new_cursor_pos, | 198 size_t new_cursor_pos, |
| 199 const string16& new_text, | 199 const base::string16& new_text, |
| 200 size_t new_text_start) | 200 size_t new_text_start) |
| 201 : Edit(REPLACE_EDIT, merge_type, | 201 : Edit(REPLACE_EDIT, merge_type, |
| 202 old_cursor_pos, | 202 old_cursor_pos, |
| 203 old_text, | 203 old_text, |
| 204 old_text_start, | 204 old_text_start, |
| 205 backward, | 205 backward, |
| 206 new_cursor_pos, | 206 new_cursor_pos, |
| 207 new_text, | 207 new_text, |
| 208 new_text_start) { | 208 new_text_start) { |
| 209 } | 209 } |
| 210 | 210 |
| 211 // Edit implementation. | 211 // Edit implementation. |
| 212 virtual bool DoMerge(const Edit* edit) OVERRIDE { | 212 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
| 213 if (edit->type() == DELETE_EDIT || | 213 if (edit->type() == DELETE_EDIT || |
| 214 new_text_end() != edit->old_text_start_ || | 214 new_text_end() != edit->old_text_start_ || |
| 215 edit->old_text_start_ != edit->new_text_start_) | 215 edit->old_text_start_ != edit->new_text_start_) |
| 216 return false; | 216 return false; |
| 217 old_text_ += edit->old_text_; | 217 old_text_ += edit->old_text_; |
| 218 new_text_ += edit->new_text_; | 218 new_text_ += edit->new_text_; |
| 219 new_cursor_pos_ = edit->new_cursor_pos_; | 219 new_cursor_pos_ = edit->new_cursor_pos_; |
| 220 return true; | 220 return true; |
| 221 } | 221 } |
| 222 }; | 222 }; |
| 223 | 223 |
| 224 class DeleteEdit : public Edit { | 224 class DeleteEdit : public Edit { |
| 225 public: | 225 public: |
| 226 DeleteEdit(bool mergeable, | 226 DeleteEdit(bool mergeable, |
| 227 const string16& text, | 227 const base::string16& text, |
| 228 size_t text_start, | 228 size_t text_start, |
| 229 bool backward) | 229 bool backward) |
| 230 : Edit(DELETE_EDIT, | 230 : Edit(DELETE_EDIT, |
| 231 mergeable ? MERGEABLE : DO_NOT_MERGE, | 231 mergeable ? MERGEABLE : DO_NOT_MERGE, |
| 232 (backward ? text_start + text.length() : text_start), | 232 (backward ? text_start + text.length() : text_start), |
| 233 text, | 233 text, |
| 234 text_start, | 234 text_start, |
| 235 backward, | 235 backward, |
| 236 text_start, | 236 text_start, |
| 237 string16(), | 237 base::string16(), |
| 238 text_start) { | 238 text_start) { |
| 239 } | 239 } |
| 240 | 240 |
| 241 // Edit implementation. | 241 // Edit implementation. |
| 242 virtual bool DoMerge(const Edit* edit) OVERRIDE { | 242 virtual bool DoMerge(const Edit* edit) OVERRIDE { |
| 243 if (edit->type() != DELETE_EDIT) | 243 if (edit->type() != DELETE_EDIT) |
| 244 return false; | 244 return false; |
| 245 | 245 |
| 246 if (delete_backward_) { | 246 if (delete_backward_) { |
| 247 // backspace can be merged only with backspace at the | 247 // backspace can be merged only with backspace at the |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 299 : delegate_(delegate), | 299 : delegate_(delegate), |
| 300 render_text_(gfx::RenderText::CreateInstance()), | 300 render_text_(gfx::RenderText::CreateInstance()), |
| 301 current_edit_(edit_history_.end()) { | 301 current_edit_(edit_history_.end()) { |
| 302 } | 302 } |
| 303 | 303 |
| 304 TextfieldViewsModel::~TextfieldViewsModel() { | 304 TextfieldViewsModel::~TextfieldViewsModel() { |
| 305 ClearEditHistory(); | 305 ClearEditHistory(); |
| 306 ClearComposition(); | 306 ClearComposition(); |
| 307 } | 307 } |
| 308 | 308 |
| 309 const string16& TextfieldViewsModel::GetText() const { | 309 const base::string16& TextfieldViewsModel::GetText() const { |
| 310 return render_text_->text(); | 310 return render_text_->text(); |
| 311 } | 311 } |
| 312 | 312 |
| 313 bool TextfieldViewsModel::SetText(const string16& text) { | 313 bool TextfieldViewsModel::SetText(const base::string16& text) { |
| 314 bool changed = false; | 314 bool changed = false; |
| 315 if (HasCompositionText()) { | 315 if (HasCompositionText()) { |
| 316 ConfirmCompositionText(); | 316 ConfirmCompositionText(); |
| 317 changed = true; | 317 changed = true; |
| 318 } | 318 } |
| 319 if (GetText() != text) { | 319 if (GetText() != text) { |
| 320 if (changed) // No need to remember composition. | 320 if (changed) // No need to remember composition. |
| 321 Undo(); | 321 Undo(); |
| 322 size_t old_cursor = GetCursorPosition(); | 322 size_t old_cursor = GetCursorPosition(); |
| 323 // SetText moves the cursor to the end. | 323 // SetText moves the cursor to the end. |
| 324 size_t new_cursor = text.length(); | 324 size_t new_cursor = text.length(); |
| 325 SelectAll(false); | 325 SelectAll(false); |
| 326 // If there is a composition text, don't merge with previous edit. | 326 // If there is a composition text, don't merge with previous edit. |
| 327 // Otherwise, force merge the edits. | 327 // Otherwise, force merge the edits. |
| 328 ExecuteAndRecordReplace( | 328 ExecuteAndRecordReplace( |
| 329 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, | 329 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, |
| 330 old_cursor, | 330 old_cursor, |
| 331 new_cursor, | 331 new_cursor, |
| 332 text, | 332 text, |
| 333 0U); | 333 0U); |
| 334 render_text_->SetCursorPosition(new_cursor); | 334 render_text_->SetCursorPosition(new_cursor); |
| 335 } | 335 } |
| 336 ClearSelection(); | 336 ClearSelection(); |
| 337 return changed; | 337 return changed; |
| 338 } | 338 } |
| 339 | 339 |
| 340 void TextfieldViewsModel::Append(const string16& text) { | 340 void TextfieldViewsModel::Append(const base::string16& text) { |
| 341 if (HasCompositionText()) | 341 if (HasCompositionText()) |
| 342 ConfirmCompositionText(); | 342 ConfirmCompositionText(); |
| 343 size_t save = GetCursorPosition(); | 343 size_t save = GetCursorPosition(); |
| 344 MoveCursor(gfx::LINE_BREAK, | 344 MoveCursor(gfx::LINE_BREAK, |
| 345 render_text_->GetVisualDirectionOfLogicalEnd(), | 345 render_text_->GetVisualDirectionOfLogicalEnd(), |
| 346 false); | 346 false); |
| 347 InsertText(text); | 347 InsertText(text); |
| 348 render_text_->SetCursorPosition(save); | 348 render_text_->SetCursorPosition(save); |
| 349 ClearSelection(); | 349 ClearSelection(); |
| 350 } | 350 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 } | 416 } |
| 417 return render_text_->MoveCursorTo(model); | 417 return render_text_->MoveCursorTo(model); |
| 418 } | 418 } |
| 419 | 419 |
| 420 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 420 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
| 421 if (HasCompositionText()) | 421 if (HasCompositionText()) |
| 422 ConfirmCompositionText(); | 422 ConfirmCompositionText(); |
| 423 return render_text_->MoveCursorTo(point, select); | 423 return render_text_->MoveCursorTo(point, select); |
| 424 } | 424 } |
| 425 | 425 |
| 426 string16 TextfieldViewsModel::GetSelectedText() const { | 426 base::string16 TextfieldViewsModel::GetSelectedText() const { |
| 427 return GetText().substr(render_text_->selection().GetMin(), | 427 return GetText().substr(render_text_->selection().GetMin(), |
| 428 render_text_->selection().length()); | 428 render_text_->selection().length()); |
| 429 } | 429 } |
| 430 | 430 |
| 431 void TextfieldViewsModel::SelectRange(const gfx::Range& range) { | 431 void TextfieldViewsModel::SelectRange(const gfx::Range& range) { |
| 432 if (HasCompositionText()) | 432 if (HasCompositionText()) |
| 433 ConfirmCompositionText(); | 433 ConfirmCompositionText(); |
| 434 render_text_->SelectRange(range); | 434 render_text_->SelectRange(range); |
| 435 } | 435 } |
| 436 | 436 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 ++iter != edit_history_.end(); | 472 ++iter != edit_history_.end(); |
| 473 } | 473 } |
| 474 | 474 |
| 475 bool TextfieldViewsModel::Undo() { | 475 bool TextfieldViewsModel::Undo() { |
| 476 if (!CanUndo()) | 476 if (!CanUndo()) |
| 477 return false; | 477 return false; |
| 478 DCHECK(!HasCompositionText()); | 478 DCHECK(!HasCompositionText()); |
| 479 if (HasCompositionText()) // safe guard for release build. | 479 if (HasCompositionText()) // safe guard for release build. |
| 480 CancelCompositionText(); | 480 CancelCompositionText(); |
| 481 | 481 |
| 482 string16 old = GetText(); | 482 base::string16 old = GetText(); |
| 483 size_t old_cursor = GetCursorPosition(); | 483 size_t old_cursor = GetCursorPosition(); |
| 484 (*current_edit_)->Commit(); | 484 (*current_edit_)->Commit(); |
| 485 (*current_edit_)->Undo(this); | 485 (*current_edit_)->Undo(this); |
| 486 | 486 |
| 487 if (current_edit_ == edit_history_.begin()) | 487 if (current_edit_ == edit_history_.begin()) |
| 488 current_edit_ = edit_history_.end(); | 488 current_edit_ = edit_history_.end(); |
| 489 else | 489 else |
| 490 current_edit_--; | 490 current_edit_--; |
| 491 return old != GetText() || old_cursor != GetCursorPosition(); | 491 return old != GetText() || old_cursor != GetCursorPosition(); |
| 492 } | 492 } |
| 493 | 493 |
| 494 bool TextfieldViewsModel::Redo() { | 494 bool TextfieldViewsModel::Redo() { |
| 495 if (!CanRedo()) | 495 if (!CanRedo()) |
| 496 return false; | 496 return false; |
| 497 DCHECK(!HasCompositionText()); | 497 DCHECK(!HasCompositionText()); |
| 498 if (HasCompositionText()) // safe guard for release build. | 498 if (HasCompositionText()) // safe guard for release build. |
| 499 CancelCompositionText(); | 499 CancelCompositionText(); |
| 500 | 500 |
| 501 if (current_edit_ == edit_history_.end()) | 501 if (current_edit_ == edit_history_.end()) |
| 502 current_edit_ = edit_history_.begin(); | 502 current_edit_ = edit_history_.begin(); |
| 503 else | 503 else |
| 504 current_edit_ ++; | 504 current_edit_ ++; |
| 505 string16 old = GetText(); | 505 base::string16 old = GetText(); |
| 506 size_t old_cursor = GetCursorPosition(); | 506 size_t old_cursor = GetCursorPosition(); |
| 507 (*current_edit_)->Redo(this); | 507 (*current_edit_)->Redo(this); |
| 508 return old != GetText() || old_cursor != GetCursorPosition(); | 508 return old != GetText() || old_cursor != GetCursorPosition(); |
| 509 } | 509 } |
| 510 | 510 |
| 511 bool TextfieldViewsModel::Cut() { | 511 bool TextfieldViewsModel::Cut() { |
| 512 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { | 512 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { |
| 513 ui::ScopedClipboardWriter( | 513 ui::ScopedClipboardWriter( |
| 514 ui::Clipboard::GetForCurrentThread(), | 514 ui::Clipboard::GetForCurrentThread(), |
| 515 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); | 515 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 530 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { | 530 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { |
| 531 ui::ScopedClipboardWriter( | 531 ui::ScopedClipboardWriter( |
| 532 ui::Clipboard::GetForCurrentThread(), | 532 ui::Clipboard::GetForCurrentThread(), |
| 533 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); | 533 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); |
| 534 return true; | 534 return true; |
| 535 } | 535 } |
| 536 return false; | 536 return false; |
| 537 } | 537 } |
| 538 | 538 |
| 539 bool TextfieldViewsModel::Paste() { | 539 bool TextfieldViewsModel::Paste() { |
| 540 string16 result; | 540 base::string16 result; |
| 541 ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, | 541 ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, |
| 542 &result); | 542 &result); |
| 543 if (!result.empty()) { | 543 if (!result.empty()) { |
| 544 InsertTextInternal(result, false); | 544 InsertTextInternal(result, false); |
| 545 return true; | 545 return true; |
| 546 } | 546 } |
| 547 return false; | 547 return false; |
| 548 } | 548 } |
| 549 | 549 |
| 550 bool TextfieldViewsModel::HasSelection() const { | 550 bool TextfieldViewsModel::HasSelection() const { |
| 551 return !render_text_->selection().is_empty(); | 551 return !render_text_->selection().is_empty(); |
| 552 } | 552 } |
| 553 | 553 |
| 554 void TextfieldViewsModel::DeleteSelection() { | 554 void TextfieldViewsModel::DeleteSelection() { |
| 555 DCHECK(!HasCompositionText()); | 555 DCHECK(!HasCompositionText()); |
| 556 DCHECK(HasSelection()); | 556 DCHECK(HasSelection()); |
| 557 ExecuteAndRecordDelete(render_text_->selection(), false); | 557 ExecuteAndRecordDelete(render_text_->selection(), false); |
| 558 } | 558 } |
| 559 | 559 |
| 560 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 560 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| 561 const string16& text, size_t position) { | 561 const base::string16& text, size_t position) { |
| 562 if (HasCompositionText()) | 562 if (HasCompositionText()) |
| 563 CancelCompositionText(); | 563 CancelCompositionText(); |
| 564 ExecuteAndRecordReplace(DO_NOT_MERGE, | 564 ExecuteAndRecordReplace(DO_NOT_MERGE, |
| 565 GetCursorPosition(), | 565 GetCursorPosition(), |
| 566 position + text.length(), | 566 position + text.length(), |
| 567 text, | 567 text, |
| 568 position); | 568 position); |
| 569 } | 569 } |
| 570 | 570 |
| 571 string16 TextfieldViewsModel::GetTextFromRange(const gfx::Range& range) const { | 571 base::string16 TextfieldViewsModel::GetTextFromRange( |
| 572 const gfx::Range& range) const { |
| 572 if (range.IsValid() && range.GetMin() < GetText().length()) | 573 if (range.IsValid() && range.GetMin() < GetText().length()) |
| 573 return GetText().substr(range.GetMin(), range.length()); | 574 return GetText().substr(range.GetMin(), range.length()); |
| 574 return string16(); | 575 return base::string16(); |
| 575 } | 576 } |
| 576 | 577 |
| 577 void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { | 578 void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { |
| 578 *range = gfx::Range(0, GetText().length()); | 579 *range = gfx::Range(0, GetText().length()); |
| 579 } | 580 } |
| 580 | 581 |
| 581 void TextfieldViewsModel::SetCompositionText( | 582 void TextfieldViewsModel::SetCompositionText( |
| 582 const ui::CompositionText& composition) { | 583 const ui::CompositionText& composition) { |
| 583 if (HasCompositionText()) | 584 if (HasCompositionText()) |
| 584 CancelCompositionText(); | 585 CancelCompositionText(); |
| 585 else if (HasSelection()) | 586 else if (HasSelection()) |
| 586 DeleteSelection(); | 587 DeleteSelection(); |
| 587 | 588 |
| 588 if (composition.text.empty()) | 589 if (composition.text.empty()) |
| 589 return; | 590 return; |
| 590 | 591 |
| 591 size_t cursor = GetCursorPosition(); | 592 size_t cursor = GetCursorPosition(); |
| 592 string16 new_text = GetText(); | 593 base::string16 new_text = GetText(); |
| 593 render_text_->SetText(new_text.insert(cursor, composition.text)); | 594 render_text_->SetText(new_text.insert(cursor, composition.text)); |
| 594 gfx::Range range(cursor, cursor + composition.text.length()); | 595 gfx::Range range(cursor, cursor + composition.text.length()); |
| 595 render_text_->SetCompositionRange(range); | 596 render_text_->SetCompositionRange(range); |
| 596 gfx::Range emphasized_range = GetFirstEmphasizedRange(composition); | 597 gfx::Range emphasized_range = GetFirstEmphasizedRange(composition); |
| 597 if (emphasized_range.IsValid()) { | 598 if (emphasized_range.IsValid()) { |
| 598 // This is a workaround due to the lack of support in RenderText to draw | 599 // This is a workaround due to the lack of support in RenderText to draw |
| 599 // a thick underline. In a composition returned from an IME, the segment | 600 // a thick underline. In a composition returned from an IME, the segment |
| 600 // emphasized by a thick underline usually represents the target clause. | 601 // emphasized by a thick underline usually represents the target clause. |
| 601 // Because the target clause is more important than the actual selection | 602 // Because the target clause is more important than the actual selection |
| 602 // range (or caret position) in the composition here we use a selection-like | 603 // range (or caret position) in the composition here we use a selection-like |
| 603 // marker instead to show this range. | 604 // marker instead to show this range. |
| 604 // TODO(yukawa, msw): Support thick underline in RenderText and remove | 605 // TODO(yukawa, msw): Support thick underline in RenderText and remove |
| 605 // this workaround. | 606 // this workaround. |
| 606 render_text_->SelectRange(gfx::Range( | 607 render_text_->SelectRange(gfx::Range( |
| 607 cursor + emphasized_range.GetMin(), | 608 cursor + emphasized_range.GetMin(), |
| 608 cursor + emphasized_range.GetMax())); | 609 cursor + emphasized_range.GetMax())); |
| 609 } else if (!composition.selection.is_empty()) { | 610 } else if (!composition.selection.is_empty()) { |
| 610 render_text_->SelectRange(gfx::Range( | 611 render_text_->SelectRange(gfx::Range( |
| 611 cursor + composition.selection.GetMin(), | 612 cursor + composition.selection.GetMin(), |
| 612 cursor + composition.selection.GetMax())); | 613 cursor + composition.selection.GetMax())); |
| 613 } else { | 614 } else { |
| 614 render_text_->SetCursorPosition(cursor + composition.selection.end()); | 615 render_text_->SetCursorPosition(cursor + composition.selection.end()); |
| 615 } | 616 } |
| 616 } | 617 } |
| 617 | 618 |
| 618 void TextfieldViewsModel::ConfirmCompositionText() { | 619 void TextfieldViewsModel::ConfirmCompositionText() { |
| 619 DCHECK(HasCompositionText()); | 620 DCHECK(HasCompositionText()); |
| 620 gfx::Range range = render_text_->GetCompositionRange(); | 621 gfx::Range range = render_text_->GetCompositionRange(); |
| 621 string16 text = GetText().substr(range.start(), range.length()); | 622 base::string16 text = GetText().substr(range.start(), range.length()); |
| 622 // TODO(oshima): current behavior on ChromeOS is a bit weird and not | 623 // TODO(oshima): current behavior on ChromeOS is a bit weird and not |
| 623 // sure exactly how this should work. Find out and fix if necessary. | 624 // sure exactly how this should work. Find out and fix if necessary. |
| 624 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); | 625 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
| 625 render_text_->SetCursorPosition(range.end()); | 626 render_text_->SetCursorPosition(range.end()); |
| 626 ClearComposition(); | 627 ClearComposition(); |
| 627 if (delegate_) | 628 if (delegate_) |
| 628 delegate_->OnCompositionTextConfirmedOrCleared(); | 629 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 629 } | 630 } |
| 630 | 631 |
| 631 void TextfieldViewsModel::CancelCompositionText() { | 632 void TextfieldViewsModel::CancelCompositionText() { |
| 632 DCHECK(HasCompositionText()); | 633 DCHECK(HasCompositionText()); |
| 633 gfx::Range range = render_text_->GetCompositionRange(); | 634 gfx::Range range = render_text_->GetCompositionRange(); |
| 634 ClearComposition(); | 635 ClearComposition(); |
| 635 string16 new_text = GetText(); | 636 base::string16 new_text = GetText(); |
| 636 render_text_->SetText(new_text.erase(range.start(), range.length())); | 637 render_text_->SetText(new_text.erase(range.start(), range.length())); |
| 637 render_text_->SetCursorPosition(range.start()); | 638 render_text_->SetCursorPosition(range.start()); |
| 638 if (delegate_) | 639 if (delegate_) |
| 639 delegate_->OnCompositionTextConfirmedOrCleared(); | 640 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 640 } | 641 } |
| 641 | 642 |
| 642 void TextfieldViewsModel::ClearComposition() { | 643 void TextfieldViewsModel::ClearComposition() { |
| 643 render_text_->SetCompositionRange(gfx::Range::InvalidRange()); | 644 render_text_->SetCompositionRange(gfx::Range::InvalidRange()); |
| 644 } | 645 } |
| 645 | 646 |
| 646 void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { | 647 void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { |
| 647 *range = gfx::Range(render_text_->GetCompositionRange()); | 648 *range = gfx::Range(render_text_->GetCompositionRange()); |
| 648 } | 649 } |
| 649 | 650 |
| 650 bool TextfieldViewsModel::HasCompositionText() const { | 651 bool TextfieldViewsModel::HasCompositionText() const { |
| 651 return !render_text_->GetCompositionRange().is_empty(); | 652 return !render_text_->GetCompositionRange().is_empty(); |
| 652 } | 653 } |
| 653 | 654 |
| 654 ///////////////////////////////////////////////////////////////// | 655 ///////////////////////////////////////////////////////////////// |
| 655 // TextfieldViewsModel: private | 656 // TextfieldViewsModel: private |
| 656 | 657 |
| 657 void TextfieldViewsModel::InsertTextInternal(const string16& text, | 658 void TextfieldViewsModel::InsertTextInternal(const base::string16& text, |
| 658 bool mergeable) { | 659 bool mergeable) { |
| 659 if (HasCompositionText()) { | 660 if (HasCompositionText()) { |
| 660 CancelCompositionText(); | 661 CancelCompositionText(); |
| 661 ExecuteAndRecordInsert(text, mergeable); | 662 ExecuteAndRecordInsert(text, mergeable); |
| 662 } else if (HasSelection()) { | 663 } else if (HasSelection()) { |
| 663 ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, | 664 ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, |
| 664 text); | 665 text); |
| 665 } else { | 666 } else { |
| 666 ExecuteAndRecordInsert(text, mergeable); | 667 ExecuteAndRecordInsert(text, mergeable); |
| 667 } | 668 } |
| 668 } | 669 } |
| 669 | 670 |
| 670 void TextfieldViewsModel::ReplaceTextInternal(const string16& text, | 671 void TextfieldViewsModel::ReplaceTextInternal(const base::string16& text, |
| 671 bool mergeable) { | 672 bool mergeable) { |
| 672 if (HasCompositionText()) { | 673 if (HasCompositionText()) { |
| 673 CancelCompositionText(); | 674 CancelCompositionText(); |
| 674 } else if (!HasSelection()) { | 675 } else if (!HasSelection()) { |
| 675 size_t cursor = GetCursorPosition(); | 676 size_t cursor = GetCursorPosition(); |
| 676 const gfx::SelectionModel& model = render_text_->selection_model(); | 677 const gfx::SelectionModel& model = render_text_->selection_model(); |
| 677 // When there is no selection, the default is to replace the next grapheme | 678 // When there is no selection, the default is to replace the next grapheme |
| 678 // with |text|. So, need to find the index of next grapheme first. | 679 // with |text|. So, need to find the index of next grapheme first. |
| 679 size_t next = | 680 size_t next = |
| 680 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); | 681 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 701 } | 702 } |
| 702 EditHistory::iterator delete_start = current_edit_; | 703 EditHistory::iterator delete_start = current_edit_; |
| 703 delete_start++; | 704 delete_start++; |
| 704 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 705 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
| 705 edit_history_.erase(delete_start, edit_history_.end()); | 706 edit_history_.erase(delete_start, edit_history_.end()); |
| 706 } | 707 } |
| 707 | 708 |
| 708 void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, | 709 void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, |
| 709 bool mergeable) { | 710 bool mergeable) { |
| 710 size_t old_text_start = range.GetMin(); | 711 size_t old_text_start = range.GetMin(); |
| 711 const string16 text = GetText().substr(old_text_start, range.length()); | 712 const base::string16 text = GetText().substr(old_text_start, range.length()); |
| 712 bool backward = range.is_reversed(); | 713 bool backward = range.is_reversed(); |
| 713 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 714 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
| 714 bool delete_edit = AddOrMergeEditHistory(edit); | 715 bool delete_edit = AddOrMergeEditHistory(edit); |
| 715 edit->Redo(this); | 716 edit->Redo(this); |
| 716 if (delete_edit) | 717 if (delete_edit) |
| 717 delete edit; | 718 delete edit; |
| 718 } | 719 } |
| 719 | 720 |
| 720 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 721 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
| 721 MergeType merge_type, const string16& new_text) { | 722 MergeType merge_type, const base::string16& new_text) { |
| 722 size_t new_text_start = render_text_->selection().GetMin(); | 723 size_t new_text_start = render_text_->selection().GetMin(); |
| 723 size_t new_cursor_pos = new_text_start + new_text.length(); | 724 size_t new_cursor_pos = new_text_start + new_text.length(); |
| 724 ExecuteAndRecordReplace(merge_type, | 725 ExecuteAndRecordReplace(merge_type, |
| 725 GetCursorPosition(), | 726 GetCursorPosition(), |
| 726 new_cursor_pos, | 727 new_cursor_pos, |
| 727 new_text, | 728 new_text, |
| 728 new_text_start); | 729 new_text_start); |
| 729 } | 730 } |
| 730 | 731 |
| 731 void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type, | 732 void TextfieldViewsModel::ExecuteAndRecordReplace( |
| 732 size_t old_cursor_pos, | 733 MergeType merge_type, |
| 733 size_t new_cursor_pos, | 734 size_t old_cursor_pos, |
| 734 const string16& new_text, | 735 size_t new_cursor_pos, |
| 735 size_t new_text_start) { | 736 const base::string16& new_text, |
| 737 size_t new_text_start) { |
| 736 size_t old_text_start = render_text_->selection().GetMin(); | 738 size_t old_text_start = render_text_->selection().GetMin(); |
| 737 bool backward = render_text_->selection().is_reversed(); | 739 bool backward = render_text_->selection().is_reversed(); |
| 738 Edit* edit = new ReplaceEdit(merge_type, | 740 Edit* edit = new ReplaceEdit(merge_type, |
| 739 GetSelectedText(), | 741 GetSelectedText(), |
| 740 old_cursor_pos, | 742 old_cursor_pos, |
| 741 old_text_start, | 743 old_text_start, |
| 742 backward, | 744 backward, |
| 743 new_cursor_pos, | 745 new_cursor_pos, |
| 744 new_text, | 746 new_text, |
| 745 new_text_start); | 747 new_text_start); |
| 746 bool delete_edit = AddOrMergeEditHistory(edit); | 748 bool delete_edit = AddOrMergeEditHistory(edit); |
| 747 edit->Redo(this); | 749 edit->Redo(this); |
| 748 if (delete_edit) | 750 if (delete_edit) |
| 749 delete edit; | 751 delete edit; |
| 750 } | 752 } |
| 751 | 753 |
| 752 void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text, | 754 void TextfieldViewsModel::ExecuteAndRecordInsert(const base::string16& text, |
| 753 bool mergeable) { | 755 bool mergeable) { |
| 754 Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition()); | 756 Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition()); |
| 755 bool delete_edit = AddOrMergeEditHistory(edit); | 757 bool delete_edit = AddOrMergeEditHistory(edit); |
| 756 edit->Redo(this); | 758 edit->Redo(this); |
| 757 if (delete_edit) | 759 if (delete_edit) |
| 758 delete edit; | 760 delete edit; |
| 759 } | 761 } |
| 760 | 762 |
| 761 bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { | 763 bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { |
| 762 ClearRedoHistory(); | 764 ClearRedoHistory(); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 774 DCHECK_EQ(1u, edit_history_.size()); | 776 DCHECK_EQ(1u, edit_history_.size()); |
| 775 current_edit_ = edit_history_.begin(); | 777 current_edit_ = edit_history_.begin(); |
| 776 } else { | 778 } else { |
| 777 current_edit_++; | 779 current_edit_++; |
| 778 } | 780 } |
| 779 return false; | 781 return false; |
| 780 } | 782 } |
| 781 | 783 |
| 782 void TextfieldViewsModel::ModifyText(size_t delete_from, | 784 void TextfieldViewsModel::ModifyText(size_t delete_from, |
| 783 size_t delete_to, | 785 size_t delete_to, |
| 784 const string16& new_text, | 786 const base::string16& new_text, |
| 785 size_t new_text_insert_at, | 787 size_t new_text_insert_at, |
| 786 size_t new_cursor_pos) { | 788 size_t new_cursor_pos) { |
| 787 DCHECK_LE(delete_from, delete_to); | 789 DCHECK_LE(delete_from, delete_to); |
| 788 string16 text = GetText(); | 790 base::string16 text = GetText(); |
| 789 ClearComposition(); | 791 ClearComposition(); |
| 790 if (delete_from != delete_to) | 792 if (delete_from != delete_to) |
| 791 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 793 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
| 792 if (!new_text.empty()) | 794 if (!new_text.empty()) |
| 793 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 795 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
| 794 render_text_->SetCursorPosition(new_cursor_pos); | 796 render_text_->SetCursorPosition(new_cursor_pos); |
| 795 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 797 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). |
| 796 // This looks fine feature and we may want to do the same. | 798 // This looks fine feature and we may want to do the same. |
| 797 } | 799 } |
| 798 | 800 |
| 799 } // namespace views | 801 } // namespace views |
| OLD | NEW |