1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 #ifndef EDITABLE_WIDGET_HXX
19 #define EDITABLE_WIDGET_HXX
20 
21 #include <functional>
22 
23 #include "Widget.hxx"
24 #include "Rect.hxx"
25 #include "ContextMenu.hxx"
26 #include "UndoHandler.hxx"
27 
28 /**
29  * Base class for widgets which need to edit text, like ListWidget and
30  * EditTextWidget.
31  *
32  * Widgets wishing to enforce their own editing restrictions are able
33  * to use a 'TextFilter' as described below.
34  */
35 class EditableWidget : public Widget, public CommandSender
36 {
37   public:
38     /** Function used to test if a specified character can be inserted
39         into the internal buffer */
40     using TextFilter = std::function<bool(char)>;
41 
42     enum {
43       kAcceptCmd  = 'EDac',
44       kCancelCmd  = 'EDcl',
45       kChangedCmd = 'EDch'
46     };
47 
48   public:
49     EditableWidget(GuiObject* boss, const GUI::Font& font,
50                    int x, int y, int w, int h, const string& str = "");
51     ~EditableWidget() override = default;
52 
53     virtual void setText(const string& str, bool changed = false);
setMaxLen(int len)54     void setMaxLen(int len) { _maxLen = len; }
getText() const55     const string& getText() const { return _editString; }
56 
isEditable() const57     bool isEditable() const	{ return _editable; }
isChanged()58     bool isChanged() { return editString() != backupString(); }
59     virtual void setEditable(bool editable, bool hiliteBG = false);
60 
61     bool handleText(char text) override;
62     bool handleKeyDown(StellaKey key, StellaMod mod) override;
63 
64     // We only want to focus this widget when we can edit its contents
wantsFocus() const65     bool wantsFocus() const override { return _editable; }
66 
67     // Set filter used to test whether a character can be inserted
setTextFilter(const TextFilter & filter)68     void setTextFilter(const TextFilter& filter) { _filter = filter; }
69 
70   protected:
71     void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
72     void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
73     void handleMouseMoved(int x, int y) override;
74     void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
caretOfs() const75     virtual int caretOfs() const { return _editScrollOffset; }
76     int toCaretPos(int x) const;
77 
78     void receivedFocusWidget() override;
79     void lostFocusWidget() override;
80     void tick() override;
81     bool wantsToolTip() const override;
82 
startEditMode()83     virtual void startEditMode() { setFlags(Widget::FLAG_WANTS_RAWDATA);   }
endEditMode()84     virtual void endEditMode()   {
85       clearFlags(Widget::FLAG_WANTS_RAWDATA);
86       commit();
87     }
abortEditMode()88     virtual void abortEditMode()
89     {
90       clearFlags(Widget::FLAG_WANTS_RAWDATA);
91       abort();
92     }
commit()93     void commit() { _backupString = _editString; }
abort()94     void abort()  { setText(_backupString); }
95 
96     virtual Common::Rect getEditRect() const = 0;
97     virtual int getCaretOffset() const;
98     void drawCaretSelection();
99     bool setCaretPos(int newPos);
100     bool moveCaretPos(int direction);
101     bool adjustOffset();
102 
103     // This method is used internally by child classes wanting to
104     // access/edit the internal buffer
editString()105     string& editString() { return _editString; }
backupString()106     string& backupString() { return _backupString; }
107     const string selectString() const;
resetSelection()108     void resetSelection() { _selectSize = 0; }
109     int scrollOffset();
110 
111   private:
112     // Line editing
113     bool killChar(int direction, bool addEdit = true);
114     bool killLine(int direction);
115     bool killWord(int direction);
116     bool moveWord(int direction, bool select);
117     bool markWord();
118 
119     bool killSelectedText(bool addEdit = true);
120     int selectStartPos();
121     int selectEndPos();
122     // Clipboard
123     bool cutSelectedText();
124     bool copySelectedText();
125     bool pasteSelectedText();
126 
127     // Use the current TextFilter to insert a character into the
128     // internal buffer
129     bool tryInsertChar(char c, int pos);
130 
131     ContextMenu& mouseMenu();
132 
133   private:
134     unique_ptr<ContextMenu> myMouseMenu;
135     bool    _isDragging{false};
136 
137     bool   _editable{true};
138     string _editString;
139     string _backupString;
140     int    _maxLen{0};
141     unique_ptr<UndoHandler> myUndoHandler;
142 
143     int    _caretPos{0};
144     int    _caretTimer{0};
145     bool   _caretEnabled{true};
146 
147     // Size of current selected text
148     //    0 = no selection
149     //   <0 = selected left of caret
150     //   >0 = selected right of caret
151     int    _selectSize{0};
152 
153   protected:
154     int   _editScrollOffset{0};
155     bool  _editMode{true};
156     int   _dyText{0};
157 
158   private:
159     TextFilter _filter;
160 
161   private:
162     // Following constructors and assignment operators not supported
163     EditableWidget() = delete;
164     EditableWidget(const EditableWidget&) = delete;
165     EditableWidget(EditableWidget&&) = delete;
166     EditableWidget& operator=(const EditableWidget&) = delete;
167     EditableWidget& operator=(EditableWidget&&) = delete;
168 };
169 
170 #endif
171