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 |