// Copyright (c) 2011 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 VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ #define VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_ #pragma once #include #include #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/string16.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/composition_text.h" #include "ui/gfx/rect.h" #include "ui/gfx/render_text.h" #include "views/views_export.h" namespace gfx { class RenderText; } // namespace gfx namespace ui { class Range; } // namespace ui namespace views { namespace internal { // Internal Edit class that keeps track of edits for undo/redo. class Edit; // C++ doesn't allow forward decl enum, so let's define here. enum MergeType { // The edit should not be merged with next edit. It still may // be merged with an edit with MERGE_WITH_PREVIOUS. DO_NOT_MERGE, // The edit can be merged with next edit when possible. MERGEABLE, // Does the edit have to be merged with previous edit? // This forces the merge even if the previous edit is marked // as DO_NOT_MERGE. MERGE_WITH_PREVIOUS, }; } // namespace internal // A model that represents a text content for TextfieldViews. // It supports editing, selection and cursor manipulation. class VIEWS_EXPORT TextfieldViewsModel { public: // Delegate interface implemented by the textfield view class to provided // 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 TextfieldViewsModel(Delegate* delegate); virtual ~TextfieldViewsModel(); void set_is_password(bool is_password) { is_password_ = is_password; } // Edit related methods. const string16& GetText() const; // 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 string16& text); gfx::RenderText* render_text() { return render_text_.get(); } // Inserts given |text| at the current cursor position. // The current composition text will be cleared. void InsertText(const string16& text) { InsertTextInternal(text, false); } // Inserts a character at the current cursor position. void InsertChar(char16 c) { InsertTextInternal(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 string16& text) { ReplaceTextInternal(text, false); } // Replaces the char at the current position with given character. void ReplaceChar(char16 c) { ReplaceTextInternal(string16(&c, 1), true); } // Appends the text. // The current composition text will be confirmed. void Append(const string16& 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 MoveCursorLeft(gfx::BreakType break_type, bool select); void MoveCursorRight(gfx::BreakType break_type, bool select); // Moves the selection to the specified selection in |selection|. // If there is composition text, it will be confirmed, which will update the // selection range, and it overrides the selection_start to which the // selection will move to. bool MoveCursorTo(const gfx::SelectionModel& selection); // Helper function to call MoveCursorTo on the TextfieldViewsModel. bool MoveCursorTo(const gfx::Point& point, bool select); // Selection related method // Returns the selected text. string16 GetSelectedText() const; // Gets the selected range. void GetSelectedRange(ui::Range* range) 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 ui::Range& range); void GetSelectionModel(gfx::SelectionModel* sel) const; // The current composition text will be confirmed. // render_text_'s selection model is set to |sel|. void SelectSelectionModel(const gfx::SelectionModel& sel); // Selects all text. // The current composition text will be confirmed. void SelectAll(); // Selects the word at which the cursor is currently positioned. // 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(); // Returns visible text. If the field is password, it returns the // sequence of "*". string16 GetVisibleText() const; // 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. void Copy(); // Pastes text from the clipboard at current cursor position. Returns true // if text has changed after pasting. 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 string16& text, size_t position); // Retrieves the text content in a given range. string16 GetTextFromRange(const ui::Range& range) const; // Retrieves the range containing all text in the model. void GetTextRange(ui::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(ui::Range* range) const; // Returns true if there is composition text. bool HasCompositionText() const; private: friend class NativeTextfieldViews; friend class NativeTextfieldViewsTest; friend class TextfieldViewsModelTest; friend class UndoRedo_BasicTest; friend class UndoRedo_CutCopyPasteTest; friend class UndoRedo_ReplaceTest; friend class internal::Edit; FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_BasicTest); FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest); FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_ReplaceTest); // Returns the visible text given |start| and |end|. string16 GetVisibleText(size_t start, size_t end) const; // Insert the given |text|. |mergeable| indicates if this insert // operation can be merged to previous edit in the edit history. void InsertTextInternal(const string16& text, bool mergeable); // Replace the current text with the given |text|. |mergeable| // indicates if this replace operation can be merged to previous // edit in the edit history. void ReplaceTextInternal(const string16& text, bool mergeable); // Clears all edit history. void ClearEditHistory(); // Clears redo history. void ClearRedoHistory(); // Executes and records edit operations. void ExecuteAndRecordDelete(size_t from, size_t to, bool mergeable); void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type, const string16& text); void ExecuteAndRecordReplace(internal::MergeType merge_type, size_t old_cursor_pos, size_t new_cursor_pos, const string16& text, size_t new_text_start); void ExecuteAndRecordInsert(const string16& 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 string16& new_text, size_t new_text_insert_at, size_t new_cursor_pos); void ClearComposition(); // Pointer to a TextfieldViewsModel::Delegate instance, should be provided by // the View object. Delegate* delegate_; // The stylized text, cursor, selection, and the visual layout model. scoped_ptr render_text_; // True if the text is the password. bool is_password_; 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(TextfieldViewsModel); }; } // namespace views #endif // VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_VIEWS_MODEL_H_