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