| OLD | NEW |
| (Empty) |
| 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 | |
| 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 | |
| 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. | |
| 69 void InsertText(const base::string16& new_text) { | |
| 70 InsertTextInternal(new_text, false); | |
| 71 } | |
| 72 | |
| 73 // Inserts a character at the current cursor position. | |
| 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. | |
| 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. | |
| 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_ | |
| OLD | NEW |