Home | History | Annotate | Download | only in textfield
      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 
     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.
     80   void InsertText(const string16& text) {
     81     InsertTextInternal(text, false);
     82   }
     83 
     84   // Inserts a character at the current cursor position.
     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.
     91   void ReplaceText(const string16& text) {
     92     ReplaceTextInternal(text, false);
     93   }
     94 
     95   // Replaces the char at the current position with given character.
     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