// 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. #ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ #define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ #include #include #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "ui/base/ime/composition_text.h" #include "ui/gfx/render_text.h" #include "ui/gfx/text_constants.h" #include "ui/views/views_export.h" namespace views { namespace internal { // Internal Edit class that keeps track of edits for undo/redo. class Edit; // The types of merge behavior implemented by Edit operations. enum MergeType { // The edit should not usually be merged with next edit. DO_NOT_MERGE, // The edit should be merged with next edit when possible. MERGEABLE, // The edit should be merged with the prior edit, even if marked DO_NOT_MERGE. FORCE_MERGE, }; } // namespace internal // A model that represents text content for a views::Textfield. // It supports editing, selection and cursor manipulation. class VIEWS_EXPORT TextfieldModel { public: // Delegate interface implemented by the textfield view class to provide // additional functionalities required by the model. class VIEWS_EXPORT Delegate { public: // Called when the current composition text is confirmed or cleared. virtual void OnCompositionTextConfirmedOrCleared() = 0; protected: virtual ~Delegate(); }; explicit TextfieldModel(Delegate* delegate); virtual ~TextfieldModel(); // Edit related methods. const base::string16& text() const { return render_text_->text(); } // Sets the text. Returns true if the text has been modified. The current // composition text will be confirmed first. Setting the same text will not // add edit history because it's not user visible change nor user-initiated // change. This allow a client code to set the same text multiple times // without worrying about messing edit history. bool SetText(const base::string16& new_text); gfx::RenderText* render_text() { return render_text_.get(); } // Inserts given |new_text| at the current cursor position. // The current composition text will be cleared. void InsertText(const base::string16& new_text) { InsertTextInternal(new_text, false); } // Inserts a character at the current cursor position. void InsertChar(base::char16 c) { InsertTextInternal(base::string16(&c, 1), true); } // Replaces characters at the current position with characters in given text. // The current composition text will be cleared. void ReplaceText(const base::string16& new_text) { ReplaceTextInternal(new_text, false); } // Replaces the char at the current position with given character. void ReplaceChar(base::char16 c) { ReplaceTextInternal(base::string16(&c, 1), true); } // Appends the text. // The current composition text will be confirmed. void Append(const base::string16& new_text); // Deletes the first character after the current cursor position (as if, the // the user has pressed delete key in the textfield). Returns true if // the deletion is successful. // If there is composition text, it'll be deleted instead. bool Delete(); // Deletes the first character before the current cursor position (as if, the // the user has pressed backspace key in the textfield). Returns true if // the removal is successful. // If there is composition text, it'll be deleted instead. bool Backspace(); // Cursor related methods. // Returns the current cursor position. size_t GetCursorPosition() const; // Moves the cursor, see RenderText for additional details. // The current composition text will be confirmed. void MoveCursor(gfx::BreakType break_type, gfx::VisualCursorDirection direction, bool select); // Updates the cursor to the specified selection model. Any composition text // will be confirmed, which may alter the specified selection range start. bool MoveCursorTo(const gfx::SelectionModel& cursor); // Helper function to call MoveCursorTo on the TextfieldModel. bool MoveCursorTo(const gfx::Point& point, bool select); // Selection related methods. // Returns the selected text. base::string16 GetSelectedText() const; // The current composition text will be confirmed. The selection starts with // the range's start position, and ends with the range's end position, // therefore the cursor position becomes the end position. void SelectRange(const gfx::Range& range); // The current composition text will be confirmed. // render_text_'s selection model is set to |sel|. void SelectSelectionModel(const gfx::SelectionModel& sel); // Select the entire text range. If |reversed| is true, the range will end at // the logical beginning of the text; this generally shows the leading portion // of text that overflows its display area. // The current composition text will be confirmed. void SelectAll(bool reversed); // Selects the word at which the cursor is currently positioned. If there is a // non-empty selection, the selection bounds are extended to their nearest // word boundaries. // The current composition text will be confirmed. void SelectWord(); // Clears selection. // The current composition text will be confirmed. void ClearSelection(); // Returns true if there is an undoable edit. bool CanUndo(); // Returns true if there is an redoable edit. bool CanRedo(); // Undo edit. Returns true if undo changed the text. bool Undo(); // Redo edit. Returns true if redo changed the text. bool Redo(); // Cuts the currently selected text and puts it to clipboard. Returns true // if text has changed after cutting. bool Cut(); // Copies the currently selected text and puts it to clipboard. Returns true // if something was copied to the clipboard. bool Copy(); // Pastes text from the clipboard at current cursor position. Returns true // if any text is pasted. bool Paste(); // Tells if any text is selected, even if the selection is in composition // text. bool HasSelection() const; // Deletes the selected text. This method shouldn't be called with // composition text. void DeleteSelection(); // Deletes the selected text (if any) and insert text at given position. void DeleteSelectionAndInsertTextAt(const base::string16& new_text, size_t position); // Retrieves the text content in a given range. base::string16 GetTextFromRange(const gfx::Range& range) const; // Retrieves the range containing all text in the model. void GetTextRange(gfx::Range* range) const; // Sets composition text and attributes. If there is composition text already, // it'll be replaced by the new one. Otherwise, current selection will be // replaced. If there is no selection, the composition text will be inserted // at the insertion point. // Any changes to the model except text insertion will confirm the current // composition text. void SetCompositionText(const ui::CompositionText& composition); // Converts current composition text into final content. void ConfirmCompositionText(); // Removes current composition text. void CancelCompositionText(); // Retrieves the range of current composition text. void GetCompositionTextRange(gfx::Range* range) const; // Returns true if there is composition text. bool HasCompositionText() const; // Clears all edit history. void ClearEditHistory(); private: friend class internal::Edit; FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_BasicTest); FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest); FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_ReplaceTest); // Insert the given |new_text|. |mergeable| indicates if this insert operation // can be merged with previous edits in the history. void InsertTextInternal(const base::string16& new_text, bool mergeable); // Replace the current text with the given |new_text|. |mergeable| indicates // if this replace operation can be merged with previous edits in the history. void ReplaceTextInternal(const base::string16& new_text, bool mergeable); // Clears redo history. void ClearRedoHistory(); // Executes and records edit operations. void ExecuteAndRecordDelete(gfx::Range range, bool mergeable); void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type, const base::string16& new_text); void ExecuteAndRecordReplace(internal::MergeType merge_type, size_t old_cursor_pos, size_t new_cursor_pos, const base::string16& new_text, size_t new_text_start); void ExecuteAndRecordInsert(const base::string16& new_text, bool mergeable); // Adds or merge |edit| into edit history. Return true if the edit // has been merged and must be deleted after redo. bool AddOrMergeEditHistory(internal::Edit* edit); // Modify the text buffer in following way: // 1) Delete the string from |delete_from| to |delte_to|. // 2) Insert the |new_text| at the index |new_text_insert_at|. // Note that the index is after deletion. // 3) Move the cursor to |new_cursor_pos|. void 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); void ClearComposition(); // The TextfieldModel::Delegate instance should be provided by the owner. Delegate* delegate_; // The stylized text, cursor, selection, and the visual layout model. scoped_ptr render_text_; typedef std::list EditHistory; EditHistory edit_history_; // An iterator that points to the current edit that can be undone. // This iterator moves from the |end()|, meaining no edit to undo, // to the last element (one before |end()|), meaning no edit to redo. // // There is no edit to undo (== end()) when: // 1) in initial state. (nothing to undo) // 2) very 1st edit is undone. // 3) all edit history is removed. // There is no edit to redo (== last element or no element) when: // 1) in initial state. (nothing to redo) // 2) new edit is added. (redo history is cleared) // 3) redone all undone edits. EditHistory::iterator current_edit_; DISALLOW_COPY_AND_ASSIGN(TextfieldModel); }; } // namespace views #endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_