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