• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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