| 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 288 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 bool TextfieldViewsModel::SetText(const base::string16& new_text) { | 309 const base::string16& TextfieldViewsModel::GetText() const { |
| 310 return render_text_->text(); |
| 311 } |
| 312 |
| 313 bool TextfieldViewsModel::SetText(const base::string16& text) { |
| 310 bool changed = false; | 314 bool changed = false; |
| 311 if (HasCompositionText()) { | 315 if (HasCompositionText()) { |
| 312 ConfirmCompositionText(); | 316 ConfirmCompositionText(); |
| 313 changed = true; | 317 changed = true; |
| 314 } | 318 } |
| 315 if (text() != new_text) { | 319 if (GetText() != text) { |
| 316 if (changed) // No need to remember composition. | 320 if (changed) // No need to remember composition. |
| 317 Undo(); | 321 Undo(); |
| 318 size_t old_cursor = GetCursorPosition(); | 322 size_t old_cursor = GetCursorPosition(); |
| 319 // SetText moves the cursor to the end. | 323 // SetText moves the cursor to the end. |
| 320 size_t new_cursor = new_text.length(); | 324 size_t new_cursor = text.length(); |
| 321 SelectAll(false); | 325 SelectAll(false); |
| 322 // 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. |
| 323 // Otherwise, force merge the edits. | 327 // Otherwise, force merge the edits. |
| 324 ExecuteAndRecordReplace( | 328 ExecuteAndRecordReplace( |
| 325 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, | 329 changed ? DO_NOT_MERGE : MERGE_WITH_PREVIOUS, |
| 326 old_cursor, | 330 old_cursor, |
| 327 new_cursor, | 331 new_cursor, |
| 328 new_text, | 332 text, |
| 329 0U); | 333 0U); |
| 330 render_text_->SetCursorPosition(new_cursor); | 334 render_text_->SetCursorPosition(new_cursor); |
| 331 } | 335 } |
| 332 ClearSelection(); | 336 ClearSelection(); |
| 333 return changed; | 337 return changed; |
| 334 } | 338 } |
| 335 | 339 |
| 336 void TextfieldViewsModel::Append(const base::string16& new_text) { | 340 void TextfieldViewsModel::Append(const base::string16& text) { |
| 337 if (HasCompositionText()) | 341 if (HasCompositionText()) |
| 338 ConfirmCompositionText(); | 342 ConfirmCompositionText(); |
| 339 size_t save = GetCursorPosition(); | 343 size_t save = GetCursorPosition(); |
| 340 MoveCursor(gfx::LINE_BREAK, | 344 MoveCursor(gfx::LINE_BREAK, |
| 341 render_text_->GetVisualDirectionOfLogicalEnd(), | 345 render_text_->GetVisualDirectionOfLogicalEnd(), |
| 342 false); | 346 false); |
| 343 InsertText(new_text); | 347 InsertText(text); |
| 344 render_text_->SetCursorPosition(save); | 348 render_text_->SetCursorPosition(save); |
| 345 ClearSelection(); | 349 ClearSelection(); |
| 346 } | 350 } |
| 347 | 351 |
| 348 bool TextfieldViewsModel::Delete() { | 352 bool TextfieldViewsModel::Delete() { |
| 349 if (HasCompositionText()) { | 353 if (HasCompositionText()) { |
| 350 // No undo/redo for composition text. | 354 // No undo/redo for composition text. |
| 351 CancelCompositionText(); | 355 CancelCompositionText(); |
| 352 return true; | 356 return true; |
| 353 } | 357 } |
| 354 if (HasSelection()) { | 358 if (HasSelection()) { |
| 355 DeleteSelection(); | 359 DeleteSelection(); |
| 356 return true; | 360 return true; |
| 357 } | 361 } |
| 358 if (text().length() > GetCursorPosition()) { | 362 if (GetText().length() > GetCursorPosition()) { |
| 359 size_t cursor_position = GetCursorPosition(); | 363 size_t cursor_position = GetCursorPosition(); |
| 360 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( | 364 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( |
| 361 cursor_position, gfx::CURSOR_FORWARD); | 365 cursor_position, gfx::CURSOR_FORWARD); |
| 362 ExecuteAndRecordDelete(gfx::Range(cursor_position, next_grapheme_index), | 366 ExecuteAndRecordDelete(gfx::Range(cursor_position, next_grapheme_index), |
| 363 true); | 367 true); |
| 364 return true; | 368 return true; |
| 365 } | 369 } |
| 366 return false; | 370 return false; |
| 367 } | 371 } |
| 368 | 372 |
| 369 bool TextfieldViewsModel::Backspace() { | 373 bool TextfieldViewsModel::Backspace() { |
| 370 if (HasCompositionText()) { | 374 if (HasCompositionText()) { |
| 371 // No undo/redo for composition text. | 375 // No undo/redo for composition text. |
| 372 CancelCompositionText(); | 376 CancelCompositionText(); |
| 373 return true; | 377 return true; |
| 374 } | 378 } |
| 375 if (HasSelection()) { | 379 if (HasSelection()) { |
| 376 DeleteSelection(); | 380 DeleteSelection(); |
| 377 return true; | 381 return true; |
| 378 } | 382 } |
| 379 size_t cursor_position = GetCursorPosition(); | 383 size_t cursor_position = GetCursorPosition(); |
| 380 if (cursor_position > 0) { | 384 if (cursor_position > 0) { |
| 381 // Delete one code point, which may be two UTF-16 words. | 385 // Delete one code point, which may be two UTF-16 words. |
| 382 size_t previous_char = gfx::UTF16OffsetToIndex(text(), cursor_position, -1); | 386 size_t previous_char = |
| 387 gfx::UTF16OffsetToIndex(GetText(), cursor_position, -1); |
| 383 ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true); | 388 ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true); |
| 384 return true; | 389 return true; |
| 385 } | 390 } |
| 386 return false; | 391 return false; |
| 387 } | 392 } |
| 388 | 393 |
| 389 size_t TextfieldViewsModel::GetCursorPosition() const { | 394 size_t TextfieldViewsModel::GetCursorPosition() const { |
| 390 return render_text_->cursor_position(); | 395 return render_text_->cursor_position(); |
| 391 } | 396 } |
| 392 | 397 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 412 return render_text_->MoveCursorTo(model); | 417 return render_text_->MoveCursorTo(model); |
| 413 } | 418 } |
| 414 | 419 |
| 415 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { | 420 bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) { |
| 416 if (HasCompositionText()) | 421 if (HasCompositionText()) |
| 417 ConfirmCompositionText(); | 422 ConfirmCompositionText(); |
| 418 return render_text_->MoveCursorTo(point, select); | 423 return render_text_->MoveCursorTo(point, select); |
| 419 } | 424 } |
| 420 | 425 |
| 421 base::string16 TextfieldViewsModel::GetSelectedText() const { | 426 base::string16 TextfieldViewsModel::GetSelectedText() const { |
| 422 return text().substr(render_text_->selection().GetMin(), | 427 return GetText().substr(render_text_->selection().GetMin(), |
| 423 render_text_->selection().length()); | 428 render_text_->selection().length()); |
| 424 } | 429 } |
| 425 | 430 |
| 426 void TextfieldViewsModel::SelectRange(const gfx::Range& range) { | 431 void TextfieldViewsModel::SelectRange(const gfx::Range& range) { |
| 427 if (HasCompositionText()) | 432 if (HasCompositionText()) |
| 428 ConfirmCompositionText(); | 433 ConfirmCompositionText(); |
| 429 render_text_->SelectRange(range); | 434 render_text_->SelectRange(range); |
| 430 } | 435 } |
| 431 | 436 |
| 432 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { | 437 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { |
| 433 if (HasCompositionText()) | 438 if (HasCompositionText()) |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 ++iter != edit_history_.end(); | 472 ++iter != edit_history_.end(); |
| 468 } | 473 } |
| 469 | 474 |
| 470 bool TextfieldViewsModel::Undo() { | 475 bool TextfieldViewsModel::Undo() { |
| 471 if (!CanUndo()) | 476 if (!CanUndo()) |
| 472 return false; | 477 return false; |
| 473 DCHECK(!HasCompositionText()); | 478 DCHECK(!HasCompositionText()); |
| 474 if (HasCompositionText()) // safe guard for release build. | 479 if (HasCompositionText()) // safe guard for release build. |
| 475 CancelCompositionText(); | 480 CancelCompositionText(); |
| 476 | 481 |
| 477 base::string16 old = text(); | 482 base::string16 old = GetText(); |
| 478 size_t old_cursor = GetCursorPosition(); | 483 size_t old_cursor = GetCursorPosition(); |
| 479 (*current_edit_)->Commit(); | 484 (*current_edit_)->Commit(); |
| 480 (*current_edit_)->Undo(this); | 485 (*current_edit_)->Undo(this); |
| 481 | 486 |
| 482 if (current_edit_ == edit_history_.begin()) | 487 if (current_edit_ == edit_history_.begin()) |
| 483 current_edit_ = edit_history_.end(); | 488 current_edit_ = edit_history_.end(); |
| 484 else | 489 else |
| 485 current_edit_--; | 490 current_edit_--; |
| 486 return old != text() || old_cursor != GetCursorPosition(); | 491 return old != GetText() || old_cursor != GetCursorPosition(); |
| 487 } | 492 } |
| 488 | 493 |
| 489 bool TextfieldViewsModel::Redo() { | 494 bool TextfieldViewsModel::Redo() { |
| 490 if (!CanRedo()) | 495 if (!CanRedo()) |
| 491 return false; | 496 return false; |
| 492 DCHECK(!HasCompositionText()); | 497 DCHECK(!HasCompositionText()); |
| 493 if (HasCompositionText()) // safe guard for release build. | 498 if (HasCompositionText()) // safe guard for release build. |
| 494 CancelCompositionText(); | 499 CancelCompositionText(); |
| 495 | 500 |
| 496 if (current_edit_ == edit_history_.end()) | 501 if (current_edit_ == edit_history_.end()) |
| 497 current_edit_ = edit_history_.begin(); | 502 current_edit_ = edit_history_.begin(); |
| 498 else | 503 else |
| 499 current_edit_ ++; | 504 current_edit_ ++; |
| 500 base::string16 old = text(); | 505 base::string16 old = GetText(); |
| 501 size_t old_cursor = GetCursorPosition(); | 506 size_t old_cursor = GetCursorPosition(); |
| 502 (*current_edit_)->Redo(this); | 507 (*current_edit_)->Redo(this); |
| 503 return old != text() || old_cursor != GetCursorPosition(); | 508 return old != GetText() || old_cursor != GetCursorPosition(); |
| 504 } | 509 } |
| 505 | 510 |
| 506 bool TextfieldViewsModel::Cut() { | 511 bool TextfieldViewsModel::Cut() { |
| 507 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { | 512 if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) { |
| 508 ui::ScopedClipboardWriter( | 513 ui::ScopedClipboardWriter( |
| 509 ui::Clipboard::GetForCurrentThread(), | 514 ui::Clipboard::GetForCurrentThread(), |
| 510 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); | 515 ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText()); |
| 511 // A trick to let undo/redo handle cursor correctly. | 516 // A trick to let undo/redo handle cursor correctly. |
| 512 // Undoing CUT moves the cursor to the end of the change rather | 517 // Undoing CUT moves the cursor to the end of the change rather |
| 513 // than beginning, unlike Delete/Backspace. | 518 // than beginning, unlike Delete/Backspace. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 return !render_text_->selection().is_empty(); | 551 return !render_text_->selection().is_empty(); |
| 547 } | 552 } |
| 548 | 553 |
| 549 void TextfieldViewsModel::DeleteSelection() { | 554 void TextfieldViewsModel::DeleteSelection() { |
| 550 DCHECK(!HasCompositionText()); | 555 DCHECK(!HasCompositionText()); |
| 551 DCHECK(HasSelection()); | 556 DCHECK(HasSelection()); |
| 552 ExecuteAndRecordDelete(render_text_->selection(), false); | 557 ExecuteAndRecordDelete(render_text_->selection(), false); |
| 553 } | 558 } |
| 554 | 559 |
| 555 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( | 560 void TextfieldViewsModel::DeleteSelectionAndInsertTextAt( |
| 556 const base::string16& new_text, | 561 const base::string16& text, size_t position) { |
| 557 size_t position) { | |
| 558 if (HasCompositionText()) | 562 if (HasCompositionText()) |
| 559 CancelCompositionText(); | 563 CancelCompositionText(); |
| 560 ExecuteAndRecordReplace(DO_NOT_MERGE, | 564 ExecuteAndRecordReplace(DO_NOT_MERGE, |
| 561 GetCursorPosition(), | 565 GetCursorPosition(), |
| 562 position + new_text.length(), | 566 position + text.length(), |
| 563 new_text, | 567 text, |
| 564 position); | 568 position); |
| 565 } | 569 } |
| 566 | 570 |
| 567 base::string16 TextfieldViewsModel::GetTextFromRange( | 571 base::string16 TextfieldViewsModel::GetTextFromRange( |
| 568 const gfx::Range& range) const { | 572 const gfx::Range& range) const { |
| 569 if (range.IsValid() && range.GetMin() < text().length()) | 573 if (range.IsValid() && range.GetMin() < GetText().length()) |
| 570 return text().substr(range.GetMin(), range.length()); | 574 return GetText().substr(range.GetMin(), range.length()); |
| 571 return base::string16(); | 575 return base::string16(); |
| 572 } | 576 } |
| 573 | 577 |
| 574 void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { | 578 void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { |
| 575 *range = gfx::Range(0, text().length()); | 579 *range = gfx::Range(0, GetText().length()); |
| 576 } | 580 } |
| 577 | 581 |
| 578 void TextfieldViewsModel::SetCompositionText( | 582 void TextfieldViewsModel::SetCompositionText( |
| 579 const ui::CompositionText& composition) { | 583 const ui::CompositionText& composition) { |
| 580 if (HasCompositionText()) | 584 if (HasCompositionText()) |
| 581 CancelCompositionText(); | 585 CancelCompositionText(); |
| 582 else if (HasSelection()) | 586 else if (HasSelection()) |
| 583 DeleteSelection(); | 587 DeleteSelection(); |
| 584 | 588 |
| 585 if (composition.text.empty()) | 589 if (composition.text.empty()) |
| 586 return; | 590 return; |
| 587 | 591 |
| 588 size_t cursor = GetCursorPosition(); | 592 size_t cursor = GetCursorPosition(); |
| 589 base::string16 new_text = text(); | 593 base::string16 new_text = GetText(); |
| 590 render_text_->SetText(new_text.insert(cursor, composition.text)); | 594 render_text_->SetText(new_text.insert(cursor, composition.text)); |
| 591 gfx::Range range(cursor, cursor + composition.text.length()); | 595 gfx::Range range(cursor, cursor + composition.text.length()); |
| 592 render_text_->SetCompositionRange(range); | 596 render_text_->SetCompositionRange(range); |
| 593 gfx::Range emphasized_range = GetFirstEmphasizedRange(composition); | 597 gfx::Range emphasized_range = GetFirstEmphasizedRange(composition); |
| 594 if (emphasized_range.IsValid()) { | 598 if (emphasized_range.IsValid()) { |
| 595 // 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 |
| 596 // 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 |
| 597 // emphasized by a thick underline usually represents the target clause. | 601 // emphasized by a thick underline usually represents the target clause. |
| 598 // Because the target clause is more important than the actual selection | 602 // Because the target clause is more important than the actual selection |
| 599 // 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 |
| 600 // marker instead to show this range. | 604 // marker instead to show this range. |
| 601 // TODO(yukawa, msw): Support thick underline in RenderText and remove | 605 // TODO(yukawa, msw): Support thick underline in RenderText and remove |
| 602 // this workaround. | 606 // this workaround. |
| 603 render_text_->SelectRange(gfx::Range( | 607 render_text_->SelectRange(gfx::Range( |
| 604 cursor + emphasized_range.GetMin(), | 608 cursor + emphasized_range.GetMin(), |
| 605 cursor + emphasized_range.GetMax())); | 609 cursor + emphasized_range.GetMax())); |
| 606 } else if (!composition.selection.is_empty()) { | 610 } else if (!composition.selection.is_empty()) { |
| 607 render_text_->SelectRange(gfx::Range( | 611 render_text_->SelectRange(gfx::Range( |
| 608 cursor + composition.selection.GetMin(), | 612 cursor + composition.selection.GetMin(), |
| 609 cursor + composition.selection.GetMax())); | 613 cursor + composition.selection.GetMax())); |
| 610 } else { | 614 } else { |
| 611 render_text_->SetCursorPosition(cursor + composition.selection.end()); | 615 render_text_->SetCursorPosition(cursor + composition.selection.end()); |
| 612 } | 616 } |
| 613 } | 617 } |
| 614 | 618 |
| 615 void TextfieldViewsModel::ConfirmCompositionText() { | 619 void TextfieldViewsModel::ConfirmCompositionText() { |
| 616 DCHECK(HasCompositionText()); | 620 DCHECK(HasCompositionText()); |
| 617 gfx::Range range = render_text_->GetCompositionRange(); | 621 gfx::Range range = render_text_->GetCompositionRange(); |
| 618 base::string16 composition = text().substr(range.start(), range.length()); | 622 base::string16 text = GetText().substr(range.start(), range.length()); |
| 619 // 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 |
| 620 // 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. |
| 621 AddOrMergeEditHistory(new InsertEdit(false, composition, range.start())); | 625 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
| 622 render_text_->SetCursorPosition(range.end()); | 626 render_text_->SetCursorPosition(range.end()); |
| 623 ClearComposition(); | 627 ClearComposition(); |
| 624 if (delegate_) | 628 if (delegate_) |
| 625 delegate_->OnCompositionTextConfirmedOrCleared(); | 629 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 626 } | 630 } |
| 627 | 631 |
| 628 void TextfieldViewsModel::CancelCompositionText() { | 632 void TextfieldViewsModel::CancelCompositionText() { |
| 629 DCHECK(HasCompositionText()); | 633 DCHECK(HasCompositionText()); |
| 630 gfx::Range range = render_text_->GetCompositionRange(); | 634 gfx::Range range = render_text_->GetCompositionRange(); |
| 631 ClearComposition(); | 635 ClearComposition(); |
| 632 base::string16 new_text = text(); | 636 base::string16 new_text = GetText(); |
| 633 render_text_->SetText(new_text.erase(range.start(), range.length())); | 637 render_text_->SetText(new_text.erase(range.start(), range.length())); |
| 634 render_text_->SetCursorPosition(range.start()); | 638 render_text_->SetCursorPosition(range.start()); |
| 635 if (delegate_) | 639 if (delegate_) |
| 636 delegate_->OnCompositionTextConfirmedOrCleared(); | 640 delegate_->OnCompositionTextConfirmedOrCleared(); |
| 637 } | 641 } |
| 638 | 642 |
| 639 void TextfieldViewsModel::ClearComposition() { | 643 void TextfieldViewsModel::ClearComposition() { |
| 640 render_text_->SetCompositionRange(gfx::Range::InvalidRange()); | 644 render_text_->SetCompositionRange(gfx::Range::InvalidRange()); |
| 641 } | 645 } |
| 642 | 646 |
| 643 void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { | 647 void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { |
| 644 *range = gfx::Range(render_text_->GetCompositionRange()); | 648 *range = gfx::Range(render_text_->GetCompositionRange()); |
| 645 } | 649 } |
| 646 | 650 |
| 647 bool TextfieldViewsModel::HasCompositionText() const { | 651 bool TextfieldViewsModel::HasCompositionText() const { |
| 648 return !render_text_->GetCompositionRange().is_empty(); | 652 return !render_text_->GetCompositionRange().is_empty(); |
| 649 } | 653 } |
| 650 | 654 |
| 651 void TextfieldViewsModel::ClearEditHistory() { | |
| 652 STLDeleteElements(&edit_history_); | |
| 653 current_edit_ = edit_history_.end(); | |
| 654 } | |
| 655 | |
| 656 ///////////////////////////////////////////////////////////////// | 655 ///////////////////////////////////////////////////////////////// |
| 657 // TextfieldViewsModel: private | 656 // TextfieldViewsModel: private |
| 658 | 657 |
| 659 void TextfieldViewsModel::InsertTextInternal(const base::string16& new_text, | 658 void TextfieldViewsModel::InsertTextInternal(const base::string16& text, |
| 660 bool mergeable) { | 659 bool mergeable) { |
| 661 if (HasCompositionText()) { | 660 if (HasCompositionText()) { |
| 662 CancelCompositionText(); | 661 CancelCompositionText(); |
| 663 ExecuteAndRecordInsert(new_text, mergeable); | 662 ExecuteAndRecordInsert(text, mergeable); |
| 664 } else if (HasSelection()) { | 663 } else if (HasSelection()) { |
| 665 ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, | 664 ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE, |
| 666 new_text); | 665 text); |
| 667 } else { | 666 } else { |
| 668 ExecuteAndRecordInsert(new_text, mergeable); | 667 ExecuteAndRecordInsert(text, mergeable); |
| 669 } | 668 } |
| 670 } | 669 } |
| 671 | 670 |
| 672 void TextfieldViewsModel::ReplaceTextInternal(const base::string16& new_text, | 671 void TextfieldViewsModel::ReplaceTextInternal(const base::string16& text, |
| 673 bool mergeable) { | 672 bool mergeable) { |
| 674 if (HasCompositionText()) { | 673 if (HasCompositionText()) { |
| 675 CancelCompositionText(); | 674 CancelCompositionText(); |
| 676 } else if (!HasSelection()) { | 675 } else if (!HasSelection()) { |
| 677 size_t cursor = GetCursorPosition(); | 676 size_t cursor = GetCursorPosition(); |
| 678 const gfx::SelectionModel& model = render_text_->selection_model(); | 677 const gfx::SelectionModel& model = render_text_->selection_model(); |
| 679 // 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 |
| 680 // with |new_text|. So, need to find the index of next grapheme first. | 679 // with |text|. So, need to find the index of next grapheme first. |
| 681 size_t next = | 680 size_t next = |
| 682 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); | 681 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); |
| 683 if (next == model.caret_pos()) | 682 if (next == model.caret_pos()) |
| 684 render_text_->MoveCursorTo(model); | 683 render_text_->MoveCursorTo(model); |
| 685 else | 684 else |
| 686 render_text_->SelectRange(gfx::Range(next, model.caret_pos())); | 685 render_text_->SelectRange(gfx::Range(next, model.caret_pos())); |
| 687 } | 686 } |
| 688 // Edit history is recorded in InsertText. | 687 // Edit history is recorded in InsertText. |
| 689 InsertTextInternal(new_text, mergeable); | 688 InsertTextInternal(text, mergeable); |
| 689 } |
| 690 |
| 691 void TextfieldViewsModel::ClearEditHistory() { |
| 692 STLDeleteElements(&edit_history_); |
| 693 current_edit_ = edit_history_.end(); |
| 690 } | 694 } |
| 691 | 695 |
| 692 void TextfieldViewsModel::ClearRedoHistory() { | 696 void TextfieldViewsModel::ClearRedoHistory() { |
| 693 if (edit_history_.begin() == edit_history_.end()) | 697 if (edit_history_.begin() == edit_history_.end()) |
| 694 return; | 698 return; |
| 695 if (current_edit_ == edit_history_.end()) { | 699 if (current_edit_ == edit_history_.end()) { |
| 696 ClearEditHistory(); | 700 ClearEditHistory(); |
| 697 return; | 701 return; |
| 698 } | 702 } |
| 699 EditHistory::iterator delete_start = current_edit_; | 703 EditHistory::iterator delete_start = current_edit_; |
| 700 delete_start++; | 704 delete_start++; |
| 701 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 705 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
| 702 edit_history_.erase(delete_start, edit_history_.end()); | 706 edit_history_.erase(delete_start, edit_history_.end()); |
| 703 } | 707 } |
| 704 | 708 |
| 705 void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, | 709 void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, |
| 706 bool mergeable) { | 710 bool mergeable) { |
| 707 size_t old_text_start = range.GetMin(); | 711 size_t old_text_start = range.GetMin(); |
| 708 const base::string16 old_text = text().substr(old_text_start, range.length()); | 712 const base::string16 text = GetText().substr(old_text_start, range.length()); |
| 709 bool backward = range.is_reversed(); | 713 bool backward = range.is_reversed(); |
| 710 Edit* edit = new DeleteEdit(mergeable, old_text, old_text_start, backward); | 714 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
| 711 bool delete_edit = AddOrMergeEditHistory(edit); | 715 bool delete_edit = AddOrMergeEditHistory(edit); |
| 712 edit->Redo(this); | 716 edit->Redo(this); |
| 713 if (delete_edit) | 717 if (delete_edit) |
| 714 delete edit; | 718 delete edit; |
| 715 } | 719 } |
| 716 | 720 |
| 717 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( | 721 void TextfieldViewsModel::ExecuteAndRecordReplaceSelection( |
| 718 MergeType merge_type, | 722 MergeType merge_type, const base::string16& new_text) { |
| 719 const base::string16& new_text) { | |
| 720 size_t new_text_start = render_text_->selection().GetMin(); | 723 size_t new_text_start = render_text_->selection().GetMin(); |
| 721 size_t new_cursor_pos = new_text_start + new_text.length(); | 724 size_t new_cursor_pos = new_text_start + new_text.length(); |
| 722 ExecuteAndRecordReplace(merge_type, | 725 ExecuteAndRecordReplace(merge_type, |
| 723 GetCursorPosition(), | 726 GetCursorPosition(), |
| 724 new_cursor_pos, | 727 new_cursor_pos, |
| 725 new_text, | 728 new_text, |
| 726 new_text_start); | 729 new_text_start); |
| 727 } | 730 } |
| 728 | 731 |
| 729 void TextfieldViewsModel::ExecuteAndRecordReplace( | 732 void TextfieldViewsModel::ExecuteAndRecordReplace( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 741 backward, | 744 backward, |
| 742 new_cursor_pos, | 745 new_cursor_pos, |
| 743 new_text, | 746 new_text, |
| 744 new_text_start); | 747 new_text_start); |
| 745 bool delete_edit = AddOrMergeEditHistory(edit); | 748 bool delete_edit = AddOrMergeEditHistory(edit); |
| 746 edit->Redo(this); | 749 edit->Redo(this); |
| 747 if (delete_edit) | 750 if (delete_edit) |
| 748 delete edit; | 751 delete edit; |
| 749 } | 752 } |
| 750 | 753 |
| 751 void TextfieldViewsModel::ExecuteAndRecordInsert(const base::string16& new_text, | 754 void TextfieldViewsModel::ExecuteAndRecordInsert(const base::string16& text, |
| 752 bool mergeable) { | 755 bool mergeable) { |
| 753 Edit* edit = new InsertEdit(mergeable, new_text, GetCursorPosition()); | 756 Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition()); |
| 754 bool delete_edit = AddOrMergeEditHistory(edit); | 757 bool delete_edit = AddOrMergeEditHistory(edit); |
| 755 edit->Redo(this); | 758 edit->Redo(this); |
| 756 if (delete_edit) | 759 if (delete_edit) |
| 757 delete edit; | 760 delete edit; |
| 758 } | 761 } |
| 759 | 762 |
| 760 bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { | 763 bool TextfieldViewsModel::AddOrMergeEditHistory(Edit* edit) { |
| 761 ClearRedoHistory(); | 764 ClearRedoHistory(); |
| 762 | 765 |
| 763 if (current_edit_ != edit_history_.end() && (*current_edit_)->Merge(edit)) { | 766 if (current_edit_ != edit_history_.end() && (*current_edit_)->Merge(edit)) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 777 } | 780 } |
| 778 return false; | 781 return false; |
| 779 } | 782 } |
| 780 | 783 |
| 781 void TextfieldViewsModel::ModifyText(size_t delete_from, | 784 void TextfieldViewsModel::ModifyText(size_t delete_from, |
| 782 size_t delete_to, | 785 size_t delete_to, |
| 783 const base::string16& new_text, | 786 const base::string16& new_text, |
| 784 size_t new_text_insert_at, | 787 size_t new_text_insert_at, |
| 785 size_t new_cursor_pos) { | 788 size_t new_cursor_pos) { |
| 786 DCHECK_LE(delete_from, delete_to); | 789 DCHECK_LE(delete_from, delete_to); |
| 787 base::string16 old_text = text(); | 790 base::string16 text = GetText(); |
| 788 ClearComposition(); | 791 ClearComposition(); |
| 789 if (delete_from != delete_to) | 792 if (delete_from != delete_to) |
| 790 render_text_->SetText(old_text.erase(delete_from, delete_to - delete_from)); | 793 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
| 791 if (!new_text.empty()) | 794 if (!new_text.empty()) |
| 792 render_text_->SetText(old_text.insert(new_text_insert_at, new_text)); | 795 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
| 793 render_text_->SetCursorPosition(new_cursor_pos); | 796 render_text_->SetCursorPosition(new_cursor_pos); |
| 794 // 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). |
| 795 // 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. |
| 796 } | 799 } |
| 797 | 800 |
| 798 } // namespace views | 801 } // namespace views |
| OLD | NEW |