| 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 |