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
19namespace views {
20
21namespace internal {
22// Internal Edit class that keeps track of edits for undo/redo.
23class Edit;
24
25// The types of merge behavior implemented by Edit operations.
26enum 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.
39class 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_
293