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" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
13 #include "ui/base/clipboard/clipboard.h" | 13 #include "ui/base/clipboard/clipboard.h" |
14 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 14 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
15 #include "ui/base/range/range.h" | |
16 #include "ui/base/text/utf16_indexing.h" | 15 #include "ui/base/text/utf16_indexing.h" |
17 #include "ui/gfx/canvas.h" | 16 #include "ui/gfx/canvas.h" |
18 #include "ui/gfx/font.h" | 17 #include "ui/gfx/font.h" |
| 18 #include "ui/gfx/range/range.h" |
19 #include "ui/gfx/render_text.h" | 19 #include "ui/gfx/render_text.h" |
20 #include "ui/gfx/text_constants.h" | 20 #include "ui/gfx/text_constants.h" |
21 #include "ui/views/controls/textfield/textfield.h" | 21 #include "ui/views/controls/textfield/textfield.h" |
22 | 22 |
23 namespace views { | 23 namespace views { |
24 | 24 |
25 namespace internal { | 25 namespace internal { |
26 | 26 |
27 // An edit object holds enough information/state to undo/redo the | 27 // An edit object holds enough information/state to undo/redo the |
28 // change. Two edits are merged when possible, for example, when | 28 // change. Two edits are merged when possible, for example, when |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 } | 262 } |
263 }; | 263 }; |
264 | 264 |
265 } // namespace internal | 265 } // namespace internal |
266 | 266 |
267 namespace { | 267 namespace { |
268 | 268 |
269 // Returns the first segment that is visually emphasized. Usually it's used for | 269 // Returns the first segment that is visually emphasized. Usually it's used for |
270 // representing the target clause (on Windows). Returns an invalid range if | 270 // representing the target clause (on Windows). Returns an invalid range if |
271 // there is no such a range. | 271 // there is no such a range. |
272 ui::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { | 272 gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { |
273 for (size_t i = 0; i < composition.underlines.size(); ++i) { | 273 for (size_t i = 0; i < composition.underlines.size(); ++i) { |
274 const ui::CompositionUnderline& underline = composition.underlines[i]; | 274 const ui::CompositionUnderline& underline = composition.underlines[i]; |
275 if (underline.thick) | 275 if (underline.thick) |
276 return ui::Range(underline.start_offset, underline.end_offset); | 276 return gfx::Range(underline.start_offset, underline.end_offset); |
277 } | 277 } |
278 return ui::Range::InvalidRange(); | 278 return gfx::Range::InvalidRange(); |
279 } | 279 } |
280 | 280 |
281 } // namespace | 281 } // namespace |
282 | 282 |
283 using internal::Edit; | 283 using internal::Edit; |
284 using internal::DeleteEdit; | 284 using internal::DeleteEdit; |
285 using internal::InsertEdit; | 285 using internal::InsertEdit; |
286 using internal::ReplaceEdit; | 286 using internal::ReplaceEdit; |
287 using internal::MergeType; | 287 using internal::MergeType; |
288 using internal::DO_NOT_MERGE; | 288 using internal::DO_NOT_MERGE; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 return true; | 356 return true; |
357 } | 357 } |
358 if (HasSelection()) { | 358 if (HasSelection()) { |
359 DeleteSelection(); | 359 DeleteSelection(); |
360 return true; | 360 return true; |
361 } | 361 } |
362 if (GetText().length() > GetCursorPosition()) { | 362 if (GetText().length() > GetCursorPosition()) { |
363 size_t cursor_position = GetCursorPosition(); | 363 size_t cursor_position = GetCursorPosition(); |
364 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( | 364 size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme( |
365 cursor_position, gfx::CURSOR_FORWARD); | 365 cursor_position, gfx::CURSOR_FORWARD); |
366 ExecuteAndRecordDelete(ui::Range(cursor_position, next_grapheme_index), | 366 ExecuteAndRecordDelete(gfx::Range(cursor_position, next_grapheme_index), |
367 true); | 367 true); |
368 return true; | 368 return true; |
369 } | 369 } |
370 return false; | 370 return false; |
371 } | 371 } |
372 | 372 |
373 bool TextfieldViewsModel::Backspace() { | 373 bool TextfieldViewsModel::Backspace() { |
374 if (HasCompositionText()) { | 374 if (HasCompositionText()) { |
375 // No undo/redo for composition text. | 375 // No undo/redo for composition text. |
376 CancelCompositionText(); | 376 CancelCompositionText(); |
377 return true; | 377 return true; |
378 } | 378 } |
379 if (HasSelection()) { | 379 if (HasSelection()) { |
380 DeleteSelection(); | 380 DeleteSelection(); |
381 return true; | 381 return true; |
382 } | 382 } |
383 size_t cursor_position = GetCursorPosition(); | 383 size_t cursor_position = GetCursorPosition(); |
384 if (cursor_position > 0) { | 384 if (cursor_position > 0) { |
385 // Delete one code point, which may be two UTF-16 words. | 385 // Delete one code point, which may be two UTF-16 words. |
386 size_t previous_char = | 386 size_t previous_char = |
387 ui::UTF16OffsetToIndex(GetText(), cursor_position, -1); | 387 ui::UTF16OffsetToIndex(GetText(), cursor_position, -1); |
388 ExecuteAndRecordDelete(ui::Range(cursor_position, previous_char), true); | 388 ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true); |
389 return true; | 389 return true; |
390 } | 390 } |
391 return false; | 391 return false; |
392 } | 392 } |
393 | 393 |
394 size_t TextfieldViewsModel::GetCursorPosition() const { | 394 size_t TextfieldViewsModel::GetCursorPosition() const { |
395 return render_text_->cursor_position(); | 395 return render_text_->cursor_position(); |
396 } | 396 } |
397 | 397 |
398 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, | 398 void TextfieldViewsModel::MoveCursor(gfx::BreakType break_type, |
399 gfx::VisualCursorDirection direction, | 399 gfx::VisualCursorDirection direction, |
400 bool select) { | 400 bool select) { |
401 if (HasCompositionText()) | 401 if (HasCompositionText()) |
402 ConfirmCompositionText(); | 402 ConfirmCompositionText(); |
403 render_text_->MoveCursor(break_type, direction, select); | 403 render_text_->MoveCursor(break_type, direction, select); |
404 } | 404 } |
405 | 405 |
406 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& model) { | 406 bool TextfieldViewsModel::MoveCursorTo(const gfx::SelectionModel& model) { |
407 if (HasCompositionText()) { | 407 if (HasCompositionText()) { |
408 ConfirmCompositionText(); | 408 ConfirmCompositionText(); |
409 // ConfirmCompositionText() updates cursor position. Need to reflect it in | 409 // ConfirmCompositionText() updates cursor position. Need to reflect it in |
410 // the SelectionModel parameter of MoveCursorTo(). | 410 // the SelectionModel parameter of MoveCursorTo(). |
411 ui::Range range(render_text_->selection().start(), model.caret_pos()); | 411 gfx::Range range(render_text_->selection().start(), model.caret_pos()); |
412 if (!range.is_empty()) | 412 if (!range.is_empty()) |
413 return render_text_->SelectRange(range); | 413 return render_text_->SelectRange(range); |
414 return render_text_->MoveCursorTo( | 414 return render_text_->MoveCursorTo( |
415 gfx::SelectionModel(model.caret_pos(), model.caret_affinity())); | 415 gfx::SelectionModel(model.caret_pos(), model.caret_affinity())); |
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 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 ui::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 |
437 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { | 437 void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) { |
438 if (HasCompositionText()) | 438 if (HasCompositionText()) |
439 ConfirmCompositionText(); | 439 ConfirmCompositionText(); |
440 render_text_->MoveCursorTo(sel); | 440 render_text_->MoveCursorTo(sel); |
441 } | 441 } |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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::BUFFER_STANDARD).WriteText(GetSelectedText()); | 515 ui::Clipboard::BUFFER_STANDARD).WriteText(GetSelectedText()); |
516 // A trick to let undo/redo handle cursor correctly. | 516 // A trick to let undo/redo handle cursor correctly. |
517 // 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 |
518 // than beginning, unlike Delete/Backspace. | 518 // than beginning, unlike Delete/Backspace. |
519 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, | 519 // TODO(oshima): Change Delete/Backspace to use DeleteSelection, |
520 // update DeleteEdit and remove this trick. | 520 // update DeleteEdit and remove this trick. |
521 const ui::Range& selection = render_text_->selection(); | 521 const gfx::Range& selection = render_text_->selection(); |
522 render_text_->SelectRange(ui::Range(selection.end(), selection.start())); | 522 render_text_->SelectRange(gfx::Range(selection.end(), selection.start())); |
523 DeleteSelection(); | 523 DeleteSelection(); |
524 return true; | 524 return true; |
525 } | 525 } |
526 return false; | 526 return false; |
527 } | 527 } |
528 | 528 |
529 bool TextfieldViewsModel::Copy() { | 529 bool TextfieldViewsModel::Copy() { |
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(), |
(...skipping 28 matching lines...) Expand all Loading... |
561 const string16& text, size_t position) { | 561 const 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 ui::Range& range) const { | 571 string16 TextfieldViewsModel::GetTextFromRange(const gfx::Range& range) const { |
572 if (range.IsValid() && range.GetMin() < GetText().length()) | 572 if (range.IsValid() && range.GetMin() < GetText().length()) |
573 return GetText().substr(range.GetMin(), range.length()); | 573 return GetText().substr(range.GetMin(), range.length()); |
574 return string16(); | 574 return string16(); |
575 } | 575 } |
576 | 576 |
577 void TextfieldViewsModel::GetTextRange(ui::Range* range) const { | 577 void TextfieldViewsModel::GetTextRange(gfx::Range* range) const { |
578 *range = ui::Range(0, GetText().length()); | 578 *range = gfx::Range(0, GetText().length()); |
579 } | 579 } |
580 | 580 |
581 void TextfieldViewsModel::SetCompositionText( | 581 void TextfieldViewsModel::SetCompositionText( |
582 const ui::CompositionText& composition) { | 582 const ui::CompositionText& composition) { |
583 if (HasCompositionText()) | 583 if (HasCompositionText()) |
584 CancelCompositionText(); | 584 CancelCompositionText(); |
585 else if (HasSelection()) | 585 else if (HasSelection()) |
586 DeleteSelection(); | 586 DeleteSelection(); |
587 | 587 |
588 if (composition.text.empty()) | 588 if (composition.text.empty()) |
589 return; | 589 return; |
590 | 590 |
591 size_t cursor = GetCursorPosition(); | 591 size_t cursor = GetCursorPosition(); |
592 string16 new_text = GetText(); | 592 string16 new_text = GetText(); |
593 render_text_->SetText(new_text.insert(cursor, composition.text)); | 593 render_text_->SetText(new_text.insert(cursor, composition.text)); |
594 ui::Range range(cursor, cursor + composition.text.length()); | 594 gfx::Range range(cursor, cursor + composition.text.length()); |
595 render_text_->SetCompositionRange(range); | 595 render_text_->SetCompositionRange(range); |
596 ui::Range emphasized_range = GetFirstEmphasizedRange(composition); | 596 gfx::Range emphasized_range = GetFirstEmphasizedRange(composition); |
597 if (emphasized_range.IsValid()) { | 597 if (emphasized_range.IsValid()) { |
598 // This is a workaround due to the lack of support in RenderText to draw | 598 // 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 | 599 // a thick underline. In a composition returned from an IME, the segment |
600 // emphasized by a thick underline usually represents the target clause. | 600 // emphasized by a thick underline usually represents the target clause. |
601 // Because the target clause is more important than the actual selection | 601 // 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 | 602 // range (or caret position) in the composition here we use a selection-like |
603 // marker instead to show this range. | 603 // marker instead to show this range. |
604 // TODO(yukawa, msw): Support thick underline in RenderText and remove | 604 // TODO(yukawa, msw): Support thick underline in RenderText and remove |
605 // this workaround. | 605 // this workaround. |
606 render_text_->SelectRange(ui::Range( | 606 render_text_->SelectRange(gfx::Range( |
607 cursor + emphasized_range.GetMin(), | 607 cursor + emphasized_range.GetMin(), |
608 cursor + emphasized_range.GetMax())); | 608 cursor + emphasized_range.GetMax())); |
609 } else if (!composition.selection.is_empty()) { | 609 } else if (!composition.selection.is_empty()) { |
610 render_text_->SelectRange(ui::Range( | 610 render_text_->SelectRange(gfx::Range( |
611 cursor + composition.selection.GetMin(), | 611 cursor + composition.selection.GetMin(), |
612 cursor + composition.selection.GetMax())); | 612 cursor + composition.selection.GetMax())); |
613 } else { | 613 } else { |
614 render_text_->SetCursorPosition(cursor + composition.selection.end()); | 614 render_text_->SetCursorPosition(cursor + composition.selection.end()); |
615 } | 615 } |
616 } | 616 } |
617 | 617 |
618 void TextfieldViewsModel::ConfirmCompositionText() { | 618 void TextfieldViewsModel::ConfirmCompositionText() { |
619 DCHECK(HasCompositionText()); | 619 DCHECK(HasCompositionText()); |
620 ui::Range range = render_text_->GetCompositionRange(); | 620 gfx::Range range = render_text_->GetCompositionRange(); |
621 string16 text = GetText().substr(range.start(), range.length()); | 621 string16 text = GetText().substr(range.start(), range.length()); |
622 // TODO(oshima): current behavior on ChromeOS is a bit weird and not | 622 // 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. | 623 // sure exactly how this should work. Find out and fix if necessary. |
624 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); | 624 AddOrMergeEditHistory(new InsertEdit(false, text, range.start())); |
625 render_text_->SetCursorPosition(range.end()); | 625 render_text_->SetCursorPosition(range.end()); |
626 ClearComposition(); | 626 ClearComposition(); |
627 if (delegate_) | 627 if (delegate_) |
628 delegate_->OnCompositionTextConfirmedOrCleared(); | 628 delegate_->OnCompositionTextConfirmedOrCleared(); |
629 } | 629 } |
630 | 630 |
631 void TextfieldViewsModel::CancelCompositionText() { | 631 void TextfieldViewsModel::CancelCompositionText() { |
632 DCHECK(HasCompositionText()); | 632 DCHECK(HasCompositionText()); |
633 ui::Range range = render_text_->GetCompositionRange(); | 633 gfx::Range range = render_text_->GetCompositionRange(); |
634 ClearComposition(); | 634 ClearComposition(); |
635 string16 new_text = GetText(); | 635 string16 new_text = GetText(); |
636 render_text_->SetText(new_text.erase(range.start(), range.length())); | 636 render_text_->SetText(new_text.erase(range.start(), range.length())); |
637 render_text_->SetCursorPosition(range.start()); | 637 render_text_->SetCursorPosition(range.start()); |
638 if (delegate_) | 638 if (delegate_) |
639 delegate_->OnCompositionTextConfirmedOrCleared(); | 639 delegate_->OnCompositionTextConfirmedOrCleared(); |
640 } | 640 } |
641 | 641 |
642 void TextfieldViewsModel::ClearComposition() { | 642 void TextfieldViewsModel::ClearComposition() { |
643 render_text_->SetCompositionRange(ui::Range::InvalidRange()); | 643 render_text_->SetCompositionRange(gfx::Range::InvalidRange()); |
644 } | 644 } |
645 | 645 |
646 void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const { | 646 void TextfieldViewsModel::GetCompositionTextRange(gfx::Range* range) const { |
647 *range = ui::Range(render_text_->GetCompositionRange()); | 647 *range = gfx::Range(render_text_->GetCompositionRange()); |
648 } | 648 } |
649 | 649 |
650 bool TextfieldViewsModel::HasCompositionText() const { | 650 bool TextfieldViewsModel::HasCompositionText() const { |
651 return !render_text_->GetCompositionRange().is_empty(); | 651 return !render_text_->GetCompositionRange().is_empty(); |
652 } | 652 } |
653 | 653 |
654 ///////////////////////////////////////////////////////////////// | 654 ///////////////////////////////////////////////////////////////// |
655 // TextfieldViewsModel: private | 655 // TextfieldViewsModel: private |
656 | 656 |
657 void TextfieldViewsModel::InsertTextInternal(const string16& text, | 657 void TextfieldViewsModel::InsertTextInternal(const string16& text, |
(...skipping 16 matching lines...) Expand all Loading... |
674 } else if (!HasSelection()) { | 674 } else if (!HasSelection()) { |
675 size_t cursor = GetCursorPosition(); | 675 size_t cursor = GetCursorPosition(); |
676 const gfx::SelectionModel& model = render_text_->selection_model(); | 676 const gfx::SelectionModel& model = render_text_->selection_model(); |
677 // When there is no selection, the default is to replace the next grapheme | 677 // 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. | 678 // with |text|. So, need to find the index of next grapheme first. |
679 size_t next = | 679 size_t next = |
680 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); | 680 render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD); |
681 if (next == model.caret_pos()) | 681 if (next == model.caret_pos()) |
682 render_text_->MoveCursorTo(model); | 682 render_text_->MoveCursorTo(model); |
683 else | 683 else |
684 render_text_->SelectRange(ui::Range(next, model.caret_pos())); | 684 render_text_->SelectRange(gfx::Range(next, model.caret_pos())); |
685 } | 685 } |
686 // Edit history is recorded in InsertText. | 686 // Edit history is recorded in InsertText. |
687 InsertTextInternal(text, mergeable); | 687 InsertTextInternal(text, mergeable); |
688 } | 688 } |
689 | 689 |
690 void TextfieldViewsModel::ClearEditHistory() { | 690 void TextfieldViewsModel::ClearEditHistory() { |
691 STLDeleteElements(&edit_history_); | 691 STLDeleteElements(&edit_history_); |
692 current_edit_ = edit_history_.end(); | 692 current_edit_ = edit_history_.end(); |
693 } | 693 } |
694 | 694 |
695 void TextfieldViewsModel::ClearRedoHistory() { | 695 void TextfieldViewsModel::ClearRedoHistory() { |
696 if (edit_history_.begin() == edit_history_.end()) | 696 if (edit_history_.begin() == edit_history_.end()) |
697 return; | 697 return; |
698 if (current_edit_ == edit_history_.end()) { | 698 if (current_edit_ == edit_history_.end()) { |
699 ClearEditHistory(); | 699 ClearEditHistory(); |
700 return; | 700 return; |
701 } | 701 } |
702 EditHistory::iterator delete_start = current_edit_; | 702 EditHistory::iterator delete_start = current_edit_; |
703 delete_start++; | 703 delete_start++; |
704 STLDeleteContainerPointers(delete_start, edit_history_.end()); | 704 STLDeleteContainerPointers(delete_start, edit_history_.end()); |
705 edit_history_.erase(delete_start, edit_history_.end()); | 705 edit_history_.erase(delete_start, edit_history_.end()); |
706 } | 706 } |
707 | 707 |
708 void TextfieldViewsModel::ExecuteAndRecordDelete(ui::Range range, | 708 void TextfieldViewsModel::ExecuteAndRecordDelete(gfx::Range range, |
709 bool mergeable) { | 709 bool mergeable) { |
710 size_t old_text_start = range.GetMin(); | 710 size_t old_text_start = range.GetMin(); |
711 const string16 text = GetText().substr(old_text_start, range.length()); | 711 const string16 text = GetText().substr(old_text_start, range.length()); |
712 bool backward = range.is_reversed(); | 712 bool backward = range.is_reversed(); |
713 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); | 713 Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward); |
714 bool delete_edit = AddOrMergeEditHistory(edit); | 714 bool delete_edit = AddOrMergeEditHistory(edit); |
715 edit->Redo(this); | 715 edit->Redo(this); |
716 if (delete_edit) | 716 if (delete_edit) |
717 delete edit; | 717 delete edit; |
718 } | 718 } |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
790 if (delete_from != delete_to) | 790 if (delete_from != delete_to) |
791 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); | 791 render_text_->SetText(text.erase(delete_from, delete_to - delete_from)); |
792 if (!new_text.empty()) | 792 if (!new_text.empty()) |
793 render_text_->SetText(text.insert(new_text_insert_at, new_text)); | 793 render_text_->SetText(text.insert(new_text_insert_at, new_text)); |
794 render_text_->SetCursorPosition(new_cursor_pos); | 794 render_text_->SetCursorPosition(new_cursor_pos); |
795 // TODO(oshima): mac selects the text that is just undone (but gtk doesn't). | 795 // 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. | 796 // This looks fine feature and we may want to do the same. |
797 } | 797 } |
798 | 798 |
799 } // namespace views | 799 } // namespace views |
OLD | NEW |