Home | History | Annotate | Download | only in textfield
      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 
     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