Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Unified Diff: ui/views/controls/textfield/textfield_model.cc

Issue 851853002: It is time. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Trying to reup because the last upload failed. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/views/controls/textfield/textfield_model.cc
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc
deleted file mode 100644
index fba7e1176396ee6fba8bad67db7c4084c3380adf..0000000000000000000000000000000000000000
--- a/ui/views/controls/textfield/textfield_model.cc
+++ /dev/null
@@ -1,769 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/textfield/textfield_model.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "ui/base/clipboard/clipboard.h"
-#include "ui/base/clipboard/scoped_clipboard_writer.h"
-#include "ui/gfx/range/range.h"
-#include "ui/gfx/utf16_indexing.h"
-
-namespace views {
-
-namespace internal {
-
-// Edit holds state information to undo/redo editing changes. Editing operations
-// are merged when possible, like when characters are typed in sequence. Calling
-// Commit() marks an edit as an independent operation that shouldn't be merged.
-class Edit {
- public:
- enum Type {
- INSERT_EDIT,
- DELETE_EDIT,
- REPLACE_EDIT,
- };
-
- virtual ~Edit() {}
-
- // Revert the change made by this edit in |model|.
- void Undo(TextfieldModel* model) {
- model->ModifyText(new_text_start_, new_text_end(),
- old_text_, old_text_start_,
- old_cursor_pos_);
- }
-
- // Apply the change of this edit to the |model|.
- void Redo(TextfieldModel* model) {
- model->ModifyText(old_text_start_, old_text_end(),
- new_text_, new_text_start_,
- new_cursor_pos_);
- }
-
- // Try to merge the |edit| into this edit and returns true on success. The
- // merged edit will be deleted after redo and should not be reused.
- bool Merge(const Edit* edit) {
- // Don't merge if previous edit is DELETE. This happens when a
- // user deletes characters then hits return. In this case, the
- // delete should be treated as separate edit that can be undone
- // and should not be merged with the replace edit.
- if (type_ != DELETE_EDIT && edit->force_merge()) {
- MergeReplace(edit);
- return true;
- }
- return mergeable() && edit->mergeable() && DoMerge(edit);
- }
-
- // Commits the edit and marks as un-mergeable.
- void Commit() { merge_type_ = DO_NOT_MERGE; }
-
- private:
- friend class InsertEdit;
- friend class ReplaceEdit;
- friend class DeleteEdit;
-
- Edit(Type type,
- MergeType merge_type,
- size_t old_cursor_pos,
- const base::string16& old_text,
- size_t old_text_start,
- bool delete_backward,
- size_t new_cursor_pos,
- const base::string16& new_text,
- size_t new_text_start)
- : type_(type),
- merge_type_(merge_type),
- old_cursor_pos_(old_cursor_pos),
- old_text_(old_text),
- old_text_start_(old_text_start),
- delete_backward_(delete_backward),
- new_cursor_pos_(new_cursor_pos),
- new_text_(new_text),
- new_text_start_(new_text_start) {
- }
-
- // Each type of edit provides its own specific merge implementation.
- virtual bool DoMerge(const Edit* edit) = 0;
-
- Type type() const { return type_; }
-
- // Can this edit be merged?
- bool mergeable() const { return merge_type_ == MERGEABLE; }
-
- // Should this edit be forcibly merged with the previous edit?
- bool force_merge() const { return merge_type_ == FORCE_MERGE; }
-
- // Returns the end index of the |old_text_|.
- size_t old_text_end() const { return old_text_start_ + old_text_.length(); }
-
- // Returns the end index of the |new_text_|.
- size_t new_text_end() const { return new_text_start_ + new_text_.length(); }
-
- // Merge the replace edit into the current edit. This handles the special case
- // where an omnibox autocomplete string is set after a new character is typed.
- void MergeReplace(const Edit* edit) {
- CHECK_EQ(REPLACE_EDIT, edit->type_);
- CHECK_EQ(0U, edit->old_text_start_);
- CHECK_EQ(0U, edit->new_text_start_);
- base::string16 old_text = edit->old_text_;
- old_text.erase(new_text_start_, new_text_.length());
- old_text.insert(old_text_start_, old_text_);
- // SetText() replaces entire text. Set |old_text_| to the entire
- // replaced text with |this| edit undone.
- old_text_ = old_text;
- old_text_start_ = edit->old_text_start_;
- delete_backward_ = false;
-
- new_text_ = edit->new_text_;
- new_text_start_ = edit->new_text_start_;
- merge_type_ = DO_NOT_MERGE;
- }
-
- Type type_;
-
- // True if the edit can be marged.
- MergeType merge_type_;
- // Old cursor position.
- size_t old_cursor_pos_;
- // Deleted text by this edit.
- base::string16 old_text_;
- // The index of |old_text_|.
- size_t old_text_start_;
- // True if the deletion is made backward.
- bool delete_backward_;
- // New cursor position.
- size_t new_cursor_pos_;
- // Added text.
- base::string16 new_text_;
- // The index of |new_text_|
- size_t new_text_start_;
-
- DISALLOW_COPY_AND_ASSIGN(Edit);
-};
-
-class InsertEdit : public Edit {
- public:
- InsertEdit(bool mergeable, const base::string16& new_text, size_t at)
- : Edit(INSERT_EDIT,
- mergeable ? MERGEABLE : DO_NOT_MERGE,
- at /* old cursor */,
- base::string16(),
- at,
- false /* N/A */,
- at + new_text.length() /* new cursor */,
- new_text,
- at) {
- }
-
- // Edit implementation.
- virtual bool DoMerge(const Edit* edit) override {
- if (edit->type() != INSERT_EDIT || new_text_end() != edit->new_text_start_)
- return false;
- // If continuous edit, merge it.
- // TODO(oshima): gtk splits edits between whitespace. Find out what
- // we want to here and implement if necessary.
- new_text_ += edit->new_text_;
- new_cursor_pos_ = edit->new_cursor_pos_;
- return true;
- }
-};
-
-class ReplaceEdit : public Edit {
- public:
- ReplaceEdit(MergeType merge_type,
- const base::string16& old_text,
- size_t old_cursor_pos,
- size_t old_text_start,
- bool backward,
- size_t new_cursor_pos,
- const base::string16& new_text,
- size_t new_text_start)
- : Edit(REPLACE_EDIT, merge_type,
- old_cursor_pos,
- old_text,
- old_text_start,
- backward,
- new_cursor_pos,
- new_text,
- new_text_start) {
- }
-
- // Edit implementation.
- virtual bool DoMerge(const Edit* edit) override {
- if (edit->type() == DELETE_EDIT ||
- new_text_end() != edit->old_text_start_ ||
- edit->old_text_start_ != edit->new_text_start_)
- return false;
- old_text_ += edit->old_text_;
- new_text_ += edit->new_text_;
- new_cursor_pos_ = edit->new_cursor_pos_;
- return true;
- }
-};
-
-class DeleteEdit : public Edit {
- public:
- DeleteEdit(bool mergeable,
- const base::string16& text,
- size_t text_start,
- bool backward)
- : Edit(DELETE_EDIT,
- mergeable ? MERGEABLE : DO_NOT_MERGE,
- (backward ? text_start + text.length() : text_start),
- text,
- text_start,
- backward,
- text_start,
- base::string16(),
- text_start) {
- }
-
- // Edit implementation.
- virtual bool DoMerge(const Edit* edit) override {
- if (edit->type() != DELETE_EDIT)
- return false;
-
- if (delete_backward_) {
- // backspace can be merged only with backspace at the same position.
- if (!edit->delete_backward_ || old_text_start_ != edit->old_text_end())
- return false;
- old_text_start_ = edit->old_text_start_;
- old_text_ = edit->old_text_ + old_text_;
- new_cursor_pos_ = edit->new_cursor_pos_;
- } else {
- // delete can be merged only with delete at the same position.
- if (edit->delete_backward_ || old_text_start_ != edit->old_text_start_)
- return false;
- old_text_ += edit->old_text_;
- }
- return true;
- }
-};
-
-} // namespace internal
-
-namespace {
-
-// Returns the first segment that is visually emphasized. Usually it's used for
-// representing the target clause (on Windows). Returns an invalid range if
-// there is no such a range.
-gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) {
- for (size_t i = 0; i < composition.underlines.size(); ++i) {
- const ui::CompositionUnderline& underline = composition.underlines[i];
- if (underline.thick)
- return gfx::Range(underline.start_offset, underline.end_offset);
- }
- return gfx::Range::InvalidRange();
-}
-
-} // namespace
-
-using internal::Edit;
-using internal::DeleteEdit;
-using internal::InsertEdit;
-using internal::ReplaceEdit;
-using internal::MergeType;
-using internal::DO_NOT_MERGE;
-using internal::FORCE_MERGE;
-using internal::MERGEABLE;
-
-/////////////////////////////////////////////////////////////////
-// TextfieldModel: public
-
-TextfieldModel::Delegate::~Delegate() {}
-
-TextfieldModel::TextfieldModel(Delegate* delegate)
- : delegate_(delegate),
- render_text_(gfx::RenderText::CreateInstance()),
- current_edit_(edit_history_.end()) {
-}
-
-TextfieldModel::~TextfieldModel() {
- ClearEditHistory();
- ClearComposition();
-}
-
-bool TextfieldModel::SetText(const base::string16& new_text) {
- bool changed = false;
- if (HasCompositionText()) {
- ConfirmCompositionText();
- changed = true;
- }
- if (text() != new_text) {
- if (changed) // No need to remember composition.
- Undo();
- size_t old_cursor = GetCursorPosition();
- // SetText moves the cursor to the end.
- size_t new_cursor = new_text.length();
- SelectAll(false);
- // If there is a composition text, don't merge with previous edit.
- // Otherwise, force merge the edits.
- ExecuteAndRecordReplace(changed ? DO_NOT_MERGE : FORCE_MERGE,
- old_cursor, new_cursor, new_text, 0U);
- render_text_->SetCursorPosition(new_cursor);
- }
- ClearSelection();
- return changed;
-}
-
-void TextfieldModel::Append(const base::string16& new_text) {
- if (HasCompositionText())
- ConfirmCompositionText();
- size_t save = GetCursorPosition();
- MoveCursor(gfx::LINE_BREAK,
- render_text_->GetVisualDirectionOfLogicalEnd(),
- false);
- InsertText(new_text);
- render_text_->SetCursorPosition(save);
- ClearSelection();
-}
-
-bool TextfieldModel::Delete() {
- if (HasCompositionText()) {
- // No undo/redo for composition text.
- CancelCompositionText();
- return true;
- }
- if (HasSelection()) {
- DeleteSelection();
- return true;
- }
- if (text().length() > GetCursorPosition()) {
- size_t cursor_position = GetCursorPosition();
- size_t next_grapheme_index = render_text_->IndexOfAdjacentGrapheme(
- cursor_position, gfx::CURSOR_FORWARD);
- ExecuteAndRecordDelete(gfx::Range(cursor_position, next_grapheme_index),
- true);
- return true;
- }
- return false;
-}
-
-bool TextfieldModel::Backspace() {
- if (HasCompositionText()) {
- // No undo/redo for composition text.
- CancelCompositionText();
- return true;
- }
- if (HasSelection()) {
- DeleteSelection();
- return true;
- }
- size_t cursor_position = GetCursorPosition();
- if (cursor_position > 0) {
- // Delete one code point, which may be two UTF-16 words.
- size_t previous_char = gfx::UTF16OffsetToIndex(text(), cursor_position, -1);
- ExecuteAndRecordDelete(gfx::Range(cursor_position, previous_char), true);
- return true;
- }
- return false;
-}
-
-size_t TextfieldModel::GetCursorPosition() const {
- return render_text_->cursor_position();
-}
-
-void TextfieldModel::MoveCursor(gfx::BreakType break_type,
- gfx::VisualCursorDirection direction,
- bool select) {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->MoveCursor(break_type, direction, select);
-}
-
-bool TextfieldModel::MoveCursorTo(const gfx::SelectionModel& cursor) {
- if (HasCompositionText()) {
- ConfirmCompositionText();
- // ConfirmCompositionText() updates cursor position. Need to reflect it in
- // the SelectionModel parameter of MoveCursorTo().
- gfx::Range range(render_text_->selection().start(), cursor.caret_pos());
- if (!range.is_empty())
- return render_text_->SelectRange(range);
- return render_text_->MoveCursorTo(
- gfx::SelectionModel(cursor.caret_pos(), cursor.caret_affinity()));
- }
- return render_text_->MoveCursorTo(cursor);
-}
-
-bool TextfieldModel::MoveCursorTo(const gfx::Point& point, bool select) {
- if (HasCompositionText())
- ConfirmCompositionText();
- gfx::SelectionModel cursor = render_text_->FindCursorPosition(point);
- if (select)
- cursor.set_selection_start(render_text_->selection().start());
- return render_text_->MoveCursorTo(cursor);
-}
-
-base::string16 TextfieldModel::GetSelectedText() const {
- return text().substr(render_text_->selection().GetMin(),
- render_text_->selection().length());
-}
-
-void TextfieldModel::SelectRange(const gfx::Range& range) {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->SelectRange(range);
-}
-
-void TextfieldModel::SelectSelectionModel(const gfx::SelectionModel& sel) {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->MoveCursorTo(sel);
-}
-
-void TextfieldModel::SelectAll(bool reversed) {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->SelectAll(reversed);
-}
-
-void TextfieldModel::SelectWord() {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->SelectWord();
-}
-
-void TextfieldModel::ClearSelection() {
- if (HasCompositionText())
- ConfirmCompositionText();
- render_text_->ClearSelection();
-}
-
-bool TextfieldModel::CanUndo() {
- return edit_history_.size() && current_edit_ != edit_history_.end();
-}
-
-bool TextfieldModel::CanRedo() {
- if (!edit_history_.size())
- return false;
- // There is no redo iff the current edit is the last element in the history.
- EditHistory::iterator iter = current_edit_;
- return iter == edit_history_.end() || // at the top.
- ++iter != edit_history_.end();
-}
-
-bool TextfieldModel::Undo() {
- if (!CanUndo())
- return false;
- DCHECK(!HasCompositionText());
- if (HasCompositionText())
- CancelCompositionText();
-
- base::string16 old = text();
- size_t old_cursor = GetCursorPosition();
- (*current_edit_)->Commit();
- (*current_edit_)->Undo(this);
-
- if (current_edit_ == edit_history_.begin())
- current_edit_ = edit_history_.end();
- else
- current_edit_--;
- return old != text() || old_cursor != GetCursorPosition();
-}
-
-bool TextfieldModel::Redo() {
- if (!CanRedo())
- return false;
- DCHECK(!HasCompositionText());
- if (HasCompositionText())
- CancelCompositionText();
-
- if (current_edit_ == edit_history_.end())
- current_edit_ = edit_history_.begin();
- else
- current_edit_ ++;
- base::string16 old = text();
- size_t old_cursor = GetCursorPosition();
- (*current_edit_)->Redo(this);
- return old != text() || old_cursor != GetCursorPosition();
-}
-
-bool TextfieldModel::Cut() {
- if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) {
- ui::ScopedClipboardWriter(
- ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText());
- // A trick to let undo/redo handle cursor correctly.
- // Undoing CUT moves the cursor to the end of the change rather
- // than beginning, unlike Delete/Backspace.
- // TODO(oshima): Change Delete/Backspace to use DeleteSelection,
- // update DeleteEdit and remove this trick.
- const gfx::Range& selection = render_text_->selection();
- render_text_->SelectRange(gfx::Range(selection.end(), selection.start()));
- DeleteSelection();
- return true;
- }
- return false;
-}
-
-bool TextfieldModel::Copy() {
- if (!HasCompositionText() && HasSelection() && !render_text_->obscured()) {
- ui::ScopedClipboardWriter(
- ui::CLIPBOARD_TYPE_COPY_PASTE).WriteText(GetSelectedText());
- return true;
- }
- return false;
-}
-
-bool TextfieldModel::Paste() {
- base::string16 result;
- ui::Clipboard::GetForCurrentThread()->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE,
- &result);
- if (result.empty())
- return false;
-
- InsertTextInternal(result, false);
- return true;
-}
-
-bool TextfieldModel::HasSelection() const {
- return !render_text_->selection().is_empty();
-}
-
-void TextfieldModel::DeleteSelection() {
- DCHECK(!HasCompositionText());
- DCHECK(HasSelection());
- ExecuteAndRecordDelete(render_text_->selection(), false);
-}
-
-void TextfieldModel::DeleteSelectionAndInsertTextAt(
- const base::string16& new_text,
- size_t position) {
- if (HasCompositionText())
- CancelCompositionText();
- ExecuteAndRecordReplace(DO_NOT_MERGE,
- GetCursorPosition(),
- position + new_text.length(),
- new_text,
- position);
-}
-
-base::string16 TextfieldModel::GetTextFromRange(const gfx::Range& range) const {
- if (range.IsValid() && range.GetMin() < text().length())
- return text().substr(range.GetMin(), range.length());
- return base::string16();
-}
-
-void TextfieldModel::GetTextRange(gfx::Range* range) const {
- *range = gfx::Range(0, text().length());
-}
-
-void TextfieldModel::SetCompositionText(
- const ui::CompositionText& composition) {
- if (HasCompositionText())
- CancelCompositionText();
- else if (HasSelection())
- DeleteSelection();
-
- if (composition.text.empty())
- return;
-
- size_t cursor = GetCursorPosition();
- base::string16 new_text = text();
- render_text_->SetText(new_text.insert(cursor, composition.text));
- gfx::Range range(cursor, cursor + composition.text.length());
- render_text_->SetCompositionRange(range);
- gfx::Range emphasized_range = GetFirstEmphasizedRange(composition);
- if (emphasized_range.IsValid()) {
- // This is a workaround due to the lack of support in RenderText to draw
- // a thick underline. In a composition returned from an IME, the segment
- // emphasized by a thick underline usually represents the target clause.
- // Because the target clause is more important than the actual selection
- // range (or caret position) in the composition here we use a selection-like
- // marker instead to show this range.
- // TODO(yukawa, msw): Support thick underlines and remove this workaround.
- render_text_->SelectRange(gfx::Range(
- cursor + emphasized_range.GetMin(),
- cursor + emphasized_range.GetMax()));
- } else if (!composition.selection.is_empty()) {
- render_text_->SelectRange(gfx::Range(
- cursor + composition.selection.GetMin(),
- cursor + composition.selection.GetMax()));
- } else {
- render_text_->SetCursorPosition(cursor + composition.selection.end());
- }
-}
-
-void TextfieldModel::ConfirmCompositionText() {
- DCHECK(HasCompositionText());
- gfx::Range range = render_text_->GetCompositionRange();
- base::string16 composition = text().substr(range.start(), range.length());
- // TODO(oshima): current behavior on ChromeOS is a bit weird and not
- // sure exactly how this should work. Find out and fix if necessary.
- AddOrMergeEditHistory(new InsertEdit(false, composition, range.start()));
- render_text_->SetCursorPosition(range.end());
- ClearComposition();
- if (delegate_)
- delegate_->OnCompositionTextConfirmedOrCleared();
-}
-
-void TextfieldModel::CancelCompositionText() {
- DCHECK(HasCompositionText());
- gfx::Range range = render_text_->GetCompositionRange();
- ClearComposition();
- base::string16 new_text = text();
- render_text_->SetText(new_text.erase(range.start(), range.length()));
- render_text_->SetCursorPosition(range.start());
- if (delegate_)
- delegate_->OnCompositionTextConfirmedOrCleared();
-}
-
-void TextfieldModel::ClearComposition() {
- render_text_->SetCompositionRange(gfx::Range::InvalidRange());
-}
-
-void TextfieldModel::GetCompositionTextRange(gfx::Range* range) const {
- *range = gfx::Range(render_text_->GetCompositionRange());
-}
-
-bool TextfieldModel::HasCompositionText() const {
- return !render_text_->GetCompositionRange().is_empty();
-}
-
-void TextfieldModel::ClearEditHistory() {
- STLDeleteElements(&edit_history_);
- current_edit_ = edit_history_.end();
-}
-
-/////////////////////////////////////////////////////////////////
-// TextfieldModel: private
-
-void TextfieldModel::InsertTextInternal(const base::string16& new_text,
- bool mergeable) {
- if (HasCompositionText()) {
- CancelCompositionText();
- ExecuteAndRecordInsert(new_text, mergeable);
- } else if (HasSelection()) {
- ExecuteAndRecordReplaceSelection(mergeable ? MERGEABLE : DO_NOT_MERGE,
- new_text);
- } else {
- ExecuteAndRecordInsert(new_text, mergeable);
- }
-}
-
-void TextfieldModel::ReplaceTextInternal(const base::string16& new_text,
- bool mergeable) {
- if (HasCompositionText()) {
- CancelCompositionText();
- } else if (!HasSelection()) {
- size_t cursor = GetCursorPosition();
- const gfx::SelectionModel& model = render_text_->selection_model();
- // When there is no selection, the default is to replace the next grapheme
- // with |new_text|. So, need to find the index of next grapheme first.
- size_t next =
- render_text_->IndexOfAdjacentGrapheme(cursor, gfx::CURSOR_FORWARD);
- if (next == model.caret_pos())
- render_text_->MoveCursorTo(model);
- else
- render_text_->SelectRange(gfx::Range(next, model.caret_pos()));
- }
- // Edit history is recorded in InsertText.
- InsertTextInternal(new_text, mergeable);
-}
-
-void TextfieldModel::ClearRedoHistory() {
- if (edit_history_.begin() == edit_history_.end())
- return;
- if (current_edit_ == edit_history_.end()) {
- ClearEditHistory();
- return;
- }
- EditHistory::iterator delete_start = current_edit_;
- delete_start++;
- STLDeleteContainerPointers(delete_start, edit_history_.end());
- edit_history_.erase(delete_start, edit_history_.end());
-}
-
-void TextfieldModel::ExecuteAndRecordDelete(gfx::Range range, bool mergeable) {
- size_t old_text_start = range.GetMin();
- const base::string16 old_text = text().substr(old_text_start, range.length());
- bool backward = range.is_reversed();
- Edit* edit = new DeleteEdit(mergeable, old_text, old_text_start, backward);
- bool delete_edit = AddOrMergeEditHistory(edit);
- edit->Redo(this);
- if (delete_edit)
- delete edit;
-}
-
-void TextfieldModel::ExecuteAndRecordReplaceSelection(
- MergeType merge_type,
- const base::string16& new_text) {
- size_t new_text_start = render_text_->selection().GetMin();
- size_t new_cursor_pos = new_text_start + new_text.length();
- ExecuteAndRecordReplace(merge_type,
- GetCursorPosition(),
- new_cursor_pos,
- new_text,
- new_text_start);
-}
-
-void TextfieldModel::ExecuteAndRecordReplace(MergeType merge_type,
- size_t old_cursor_pos,
- size_t new_cursor_pos,
- const base::string16& new_text,
- size_t new_text_start) {
- size_t old_text_start = render_text_->selection().GetMin();
- bool backward = render_text_->selection().is_reversed();
- Edit* edit = new ReplaceEdit(merge_type,
- GetSelectedText(),
- old_cursor_pos,
- old_text_start,
- backward,
- new_cursor_pos,
- new_text,
- new_text_start);
- bool delete_edit = AddOrMergeEditHistory(edit);
- edit->Redo(this);
- if (delete_edit)
- delete edit;
-}
-
-void TextfieldModel::ExecuteAndRecordInsert(const base::string16& new_text,
- bool mergeable) {
- Edit* edit = new InsertEdit(mergeable, new_text, GetCursorPosition());
- bool delete_edit = AddOrMergeEditHistory(edit);
- edit->Redo(this);
- if (delete_edit)
- delete edit;
-}
-
-bool TextfieldModel::AddOrMergeEditHistory(Edit* edit) {
- ClearRedoHistory();
-
- if (current_edit_ != edit_history_.end() && (*current_edit_)->Merge(edit)) {
- // If a current edit exists and has been merged with a new edit, don't add
- // to the history, and return true to delete |edit| after redo.
- return true;
- }
- edit_history_.push_back(edit);
- if (current_edit_ == edit_history_.end()) {
- // If there is no redoable edit, this is the 1st edit because RedoHistory
- // has been already deleted.
- DCHECK_EQ(1u, edit_history_.size());
- current_edit_ = edit_history_.begin();
- } else {
- current_edit_++;
- }
- return false;
-}
-
-void TextfieldModel::ModifyText(size_t delete_from,
- size_t delete_to,
- const base::string16& new_text,
- size_t new_text_insert_at,
- size_t new_cursor_pos) {
- DCHECK_LE(delete_from, delete_to);
- base::string16 old_text = text();
- ClearComposition();
- if (delete_from != delete_to)
- render_text_->SetText(old_text.erase(delete_from, delete_to - delete_from));
- if (!new_text.empty())
- render_text_->SetText(old_text.insert(new_text_insert_at, new_text));
- render_text_->SetCursorPosition(new_cursor_pos);
- // TODO(oshima): Select text that was just undone, like Mac (but not GTK).
-}
-
-} // namespace views
« no previous file with comments | « ui/views/controls/textfield/textfield_model.h ('k') | ui/views/controls/textfield/textfield_model_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698