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