• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
render_text()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.
InsertText(const string16 & text)80   void InsertText(const string16& text) {
81     InsertTextInternal(text, false);
82   }
83 
84   // Inserts a character at the current cursor position.
InsertChar(char16 c)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.
ReplaceText(const string16 & text)91   void ReplaceText(const string16& text) {
92     ReplaceTextInternal(text, false);
93   }
94 
95   // Replaces the char at the current position with given character.
ReplaceChar(char16 c)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