1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ 6 #define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ 7 8 #include <list> 9 #include <vector> 10 11 #include "base/gtest_prod_util.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/strings/string16.h" 14 #include "ui/base/ime/composition_text.h" 15 #include "ui/gfx/render_text.h" 16 #include "ui/gfx/text_constants.h" 17 #include "ui/views/views_export.h" 18 19 namespace views { 20 21 namespace internal { 22 // Internal Edit class that keeps track of edits for undo/redo. 23 class Edit; 24 25 // The types of merge behavior implemented by Edit operations. 26 enum MergeType { 27 // The edit should not usually be merged with next edit. 28 DO_NOT_MERGE, 29 // The edit should be merged with next edit when possible. 30 MERGEABLE, 31 // The edit should be merged with the prior edit, even if marked DO_NOT_MERGE. 32 FORCE_MERGE, 33 }; 34 35 } // namespace internal 36 37 // A model that represents text content for a views::Textfield. 38 // It supports editing, selection and cursor manipulation. 39 class VIEWS_EXPORT TextfieldModel { 40 public: 41 // Delegate interface implemented by the textfield view class to provide 42 // additional functionalities required by the model. 43 class VIEWS_EXPORT Delegate { 44 public: 45 // Called when the current composition text is confirmed or cleared. 46 virtual void OnCompositionTextConfirmedOrCleared() = 0; 47 48 protected: 49 virtual ~Delegate(); 50 }; 51 52 explicit TextfieldModel(Delegate* delegate); 53 virtual ~TextfieldModel(); 54 55 // Edit related methods. 56 text()57 const base::string16& text() const { return render_text_->text(); } 58 // Sets the text. Returns true if the text has been modified. The current 59 // composition text will be confirmed first. Setting the same text will not 60 // add edit history because it's not user visible change nor user-initiated 61 // change. This allow a client code to set the same text multiple times 62 // without worrying about messing edit history. 63 bool SetText(const base::string16& new_text); 64 render_text()65 gfx::RenderText* render_text() { return render_text_.get(); } 66 67 // Inserts given |new_text| at the current cursor position. 68 // The current composition text will be cleared. InsertText(const base::string16 & new_text)69 void InsertText(const base::string16& new_text) { 70 InsertTextInternal(new_text, false); 71 } 72 73 // Inserts a character at the current cursor position. InsertChar(base::char16 c)74 void InsertChar(base::char16 c) { 75 InsertTextInternal(base::string16(&c, 1), true); 76 } 77 78 // Replaces characters at the current position with characters in given text. 79 // The current composition text will be cleared. ReplaceText(const base::string16 & new_text)80 void ReplaceText(const base::string16& new_text) { 81 ReplaceTextInternal(new_text, false); 82 } 83 84 // Replaces the char at the current position with given character. ReplaceChar(base::char16 c)85 void ReplaceChar(base::char16 c) { 86 ReplaceTextInternal(base::string16(&c, 1), true); 87 } 88 89 // Appends the text. 90 // The current composition text will be confirmed. 91 void Append(const base::string16& new_text); 92 93 // Deletes the first character after the current cursor position (as if, the 94 // the user has pressed delete key in the textfield). Returns true if 95 // the deletion is successful. 96 // If there is composition text, it'll be deleted instead. 97 bool Delete(); 98 99 // Deletes the first character before the current cursor position (as if, the 100 // the user has pressed backspace key in the textfield). Returns true if 101 // the removal is successful. 102 // If there is composition text, it'll be deleted instead. 103 bool Backspace(); 104 105 // Cursor related methods. 106 107 // Returns the current cursor position. 108 size_t GetCursorPosition() const; 109 110 // Moves the cursor, see RenderText for additional details. 111 // The current composition text will be confirmed. 112 void MoveCursor(gfx::BreakType break_type, 113 gfx::VisualCursorDirection direction, 114 bool select); 115 116 // Updates the cursor to the specified selection model. Any composition text 117 // will be confirmed, which may alter the specified selection range start. 118 bool MoveCursorTo(const gfx::SelectionModel& cursor); 119 120 // Helper function to call MoveCursorTo on the TextfieldModel. 121 bool MoveCursorTo(const gfx::Point& point, bool select); 122 123 // Selection related methods. 124 125 // Returns the selected text. 126 base::string16 GetSelectedText() const; 127 128 // The current composition text will be confirmed. The selection starts with 129 // the range's start position, and ends with the range's end position, 130 // therefore the cursor position becomes the end position. 131 void SelectRange(const gfx::Range& range); 132 133 // The current composition text will be confirmed. 134 // render_text_'s selection model is set to |sel|. 135 void SelectSelectionModel(const gfx::SelectionModel& sel); 136 137 // Select the entire text range. If |reversed| is true, the range will end at 138 // the logical beginning of the text; this generally shows the leading portion 139 // of text that overflows its display area. 140 // The current composition text will be confirmed. 141 void SelectAll(bool reversed); 142 143 // Selects the word at which the cursor is currently positioned. If there is a 144 // non-empty selection, the selection bounds are extended to their nearest 145 // word boundaries. 146 // The current composition text will be confirmed. 147 void SelectWord(); 148 149 // Clears selection. 150 // The current composition text will be confirmed. 151 void ClearSelection(); 152 153 // Returns true if there is an undoable edit. 154 bool CanUndo(); 155 156 // Returns true if there is an redoable edit. 157 bool CanRedo(); 158 159 // Undo edit. Returns true if undo changed the text. 160 bool Undo(); 161 162 // Redo edit. Returns true if redo changed the text. 163 bool Redo(); 164 165 // Cuts the currently selected text and puts it to clipboard. Returns true 166 // if text has changed after cutting. 167 bool Cut(); 168 169 // Copies the currently selected text and puts it to clipboard. Returns true 170 // if something was copied to the clipboard. 171 bool Copy(); 172 173 // Pastes text from the clipboard at current cursor position. Returns true 174 // if any text is pasted. 175 bool Paste(); 176 177 // Tells if any text is selected, even if the selection is in composition 178 // text. 179 bool HasSelection() const; 180 181 // Deletes the selected text. This method shouldn't be called with 182 // composition text. 183 void DeleteSelection(); 184 185 // Deletes the selected text (if any) and insert text at given position. 186 void DeleteSelectionAndInsertTextAt(const base::string16& new_text, 187 size_t position); 188 189 // Retrieves the text content in a given range. 190 base::string16 GetTextFromRange(const gfx::Range& range) const; 191 192 // Retrieves the range containing all text in the model. 193 void GetTextRange(gfx::Range* range) const; 194 195 // Sets composition text and attributes. If there is composition text already, 196 // it'll be replaced by the new one. Otherwise, current selection will be 197 // replaced. If there is no selection, the composition text will be inserted 198 // at the insertion point. 199 // Any changes to the model except text insertion will confirm the current 200 // composition text. 201 void SetCompositionText(const ui::CompositionText& composition); 202 203 // Converts current composition text into final content. 204 void ConfirmCompositionText(); 205 206 // Removes current composition text. 207 void CancelCompositionText(); 208 209 // Retrieves the range of current composition text. 210 void GetCompositionTextRange(gfx::Range* range) const; 211 212 // Returns true if there is composition text. 213 bool HasCompositionText() const; 214 215 // Clears all edit history. 216 void ClearEditHistory(); 217 218 private: 219 friend class internal::Edit; 220 221 FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_BasicTest); 222 FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_CutCopyPasteTest); 223 FRIEND_TEST_ALL_PREFIXES(TextfieldModelTest, UndoRedo_ReplaceTest); 224 225 // Insert the given |new_text|. |mergeable| indicates if this insert operation 226 // can be merged with previous edits in the history. 227 void InsertTextInternal(const base::string16& new_text, bool mergeable); 228 229 // Replace the current text with the given |new_text|. |mergeable| indicates 230 // if this replace operation can be merged with previous edits in the history. 231 void ReplaceTextInternal(const base::string16& new_text, bool mergeable); 232 233 // Clears redo history. 234 void ClearRedoHistory(); 235 236 // Executes and records edit operations. 237 void ExecuteAndRecordDelete(gfx::Range range, bool mergeable); 238 void ExecuteAndRecordReplaceSelection(internal::MergeType merge_type, 239 const base::string16& new_text); 240 void ExecuteAndRecordReplace(internal::MergeType merge_type, 241 size_t old_cursor_pos, 242 size_t new_cursor_pos, 243 const base::string16& new_text, 244 size_t new_text_start); 245 void ExecuteAndRecordInsert(const base::string16& new_text, bool mergeable); 246 247 // Adds or merge |edit| into edit history. Return true if the edit 248 // has been merged and must be deleted after redo. 249 bool AddOrMergeEditHistory(internal::Edit* edit); 250 251 // Modify the text buffer in following way: 252 // 1) Delete the string from |delete_from| to |delte_to|. 253 // 2) Insert the |new_text| at the index |new_text_insert_at|. 254 // Note that the index is after deletion. 255 // 3) Move the cursor to |new_cursor_pos|. 256 void ModifyText(size_t delete_from, 257 size_t delete_to, 258 const base::string16& new_text, 259 size_t new_text_insert_at, 260 size_t new_cursor_pos); 261 262 void ClearComposition(); 263 264 // The TextfieldModel::Delegate instance should be provided by the owner. 265 Delegate* delegate_; 266 267 // The stylized text, cursor, selection, and the visual layout model. 268 scoped_ptr<gfx::RenderText> render_text_; 269 270 typedef std::list<internal::Edit*> EditHistory; 271 EditHistory edit_history_; 272 273 // An iterator that points to the current edit that can be undone. 274 // This iterator moves from the |end()|, meaining no edit to undo, 275 // to the last element (one before |end()|), meaning no edit to redo. 276 // 277 // There is no edit to undo (== end()) when: 278 // 1) in initial state. (nothing to undo) 279 // 2) very 1st edit is undone. 280 // 3) all edit history is removed. 281 // There is no edit to redo (== last element or no element) when: 282 // 1) in initial state. (nothing to redo) 283 // 2) new edit is added. (redo history is cleared) 284 // 3) redone all undone edits. 285 EditHistory::iterator current_edit_; 286 287 DISALLOW_COPY_AND_ASSIGN(TextfieldModel); 288 }; 289 290 } // namespace views 291 292 #endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_MODEL_H_ 293