42df64b758f2187aee62baaa1a3db4971915d0f1
[senf.git] / senf / Utils / Termlib / Editor.hh
1 // $Id$
2 //
3 // Copyright (C) 2009
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Editor public header */
25
26 #ifndef HH_SENF_Utils_Termlib_Editor_
27 #define HH_SENF_Utils_Termlib_Editor_ 1
28
29 // Custom includes
30 #include <map>
31 #include <vector>
32 #include <string>
33 #include <senf/Scheduler/ClockService.hh>
34 #include <senf/Scheduler/TimerEvent.hh>
35 #include "AbstractTerminal.hh"
36 #include "Terminfo.hh"
37
38 //#include "Editor.mpp"
39 //-/////////////////////////////////////////////////////////////////////////////////////////////////
40
41 namespace senf {
42 namespace term {
43
44     /** \brief Provide editor support terminal functionality
45
46         This base class utilizes an arbitrary AbstractTerminal and provides terminfo based
47         terminal navigation and key parsing.
48
49         All navigation is relative to the current line. The display area can then be extended
50         downwards up to a maximum of height() lines. Resetting this extended display area will
51         return to a one line area containing only the top line.
52
53         All navigation is restricted to a width() x height() area. The last column may not be
54         written to since auto-margin terminals will move the cursor to the next line when writing to
55         that column.
56
57         This base class calls v_keyReceived() which must be defined in a derived class whenever a
58         key is fully parsed.
59      */
60     class BaseEditor
61         : public AbstractTerminal::Callbacks
62     {
63     public:
64         typedef KeyParser::keycode_t keycode_t;
65
66         static unsigned const DEFAULT_KEY_TIMEOUT_MS = 500u;
67
68         explicit BaseEditor(AbstractTerminal & terminal);
69
70         void newline();                 ///< Move to beginning of a new, empty line
71         void toColumn(unsigned c);      ///< Move cursor to column \p c
72         void put(char ch);              ///< Write \p ch at current column
73         void put(std::string const & text); ///< Write \a text starting at current column
74         void clearLine();               ///< Clear current line and move cursor to first column
75         void setBold();                 ///< Set bold char display
76         void setNormal();               ///< Set normal char display
77         void maybeClrScr();             ///< Clear screen if possible
78
79         void toLine(unsigned l);        ///< Move to relative display line \a l
80         void reset();                   ///< Reset display area to single line
81
82         unsigned currentColumn() const; ///< Return number of current column
83         unsigned currentLine() const;   ///< Return number of current relative line
84         unsigned width() const;         ///< Return current screen width
85         unsigned height() const;        ///< Return current screen height
86
87     protected:
88         virtual bool cb_init();         ///< Called when terminal is initialized
89         virtual void cb_windowSizeChanged();
90                                         ///< Called whenever the terminal window size changes
91
92 #ifndef DOXYGEN
93     private:
94 #endif
95         virtual void v_keyReceived(keycode_t key) = 0; ///< Called whenever a key is received
96
97     private:
98         virtual void cb_charReceived(char c);
99
100         void keySequenceTimeout();
101         void processKeys();
102
103         void write(char ch);
104         void write(std::string const & s);
105
106         AbstractTerminal * terminal_;
107         Terminfo tifo_;
108         KeyParser keyParser_;
109         std::string inputBuffer_;
110         ClockService::clock_type keyTimeout_;
111         scheduler::TimerEvent timer_;
112         unsigned column_;
113         unsigned displayHeight_;
114         unsigned line_;
115     };
116
117     /** \brief Single line interactive text editor
118
119         LineEditor implements a single-line input widget on an arbitrary AbstractTerminal.
120
121         \li It supports all the customary editing functions
122         \li It is possible to arbitrarily assign functions to keys via a key map
123         \li LineEditor has builtin TAB completion support
124         \li The LineEditor has a built-in history
125         \li The LineEditor supports an arbitrary auxiliary display area below the input line
126         \li The LineEditor has hide() / show() support to allow editing to be temporarily
127             interrupted.
128
129         The LineEditor will query the user for an input line. When the user accepts a line,
130         LineEditor will call a user callback function. After the callback has been called, the
131         editor is disabled. To accept a new input line, call show().
132
133         \section editor_keymap The editor key map
134
135         Keys are defined in the keymap using defineKey(). The default bindings are:
136
137         <table class="senf">
138         <tr><td>\c Return</td>    <td>bindings::accept</td></tr>
139         <tr><td>\c Right</td>     <td>bindings::forwardChar</td></tr>
140         <tr><td>\c Left</td>      <td>bindings::backwardChar</td></tr>
141         <tr><td>\c Up</td>        <td>bindings::prevHistory</td></tr>
142         <tr><td>\c Down</td>      <td>bindings::nextHistory</td></tr>
143         <tr><td>\c Backspace</td> <td>bindings::backwardDeleteChar</td></tr>
144         <tr><td>\c Delete</td>    <td>bindings::deleteChar</td></tr>
145         <tr><td>\c Home</td>      <td>bindings::beginningOfLine</td></tr>
146         <tr><td>\c End</td>       <td>bindings::endOfLine</td></tr>
147         <tr><td>\c Ctrl-K</td>    <td>bindings::deleteToEndOfLine</td></tr>
148         <tr><td>\c Ctrl-A</td>    <td>bindings::beginningOfLine</td></tr>
149         <tr><td>\c Ctrl-E</td>    <td>bindings::endOfLine</td></tr>
150         <tr><td>\c Ctrl-D</td>    <td>bindings::deleteChar</td></tr>
151         <tr><td>\c Ctrl-C</td>    <td>bindings::restartEdit</td></tr>
152         <tr><td>\c Ctrl-L</td>    <td>bindings::clearScreen</td></tr>
153         </table>
154
155         See the senf::term::bindings namespace for a list of all default provided key binding
156         functions.
157
158
159         \section editor_complete Completion support
160
161         Completion support is provided by senf::term::bindings::complete(). To use the completer,
162         you need to implement a completion function and pass it as second argument to
163         bindings::complete():
164
165         \code
166         void myCompleter(senf::term::LineEditor & editor, unsigned & b, unsigned & e,
167                          std::string & prefix, std::vector<std::string> & completions)
168         {
169             // Get text to complete
170             std::string text (editor.text().substr(b, e-b));
171
172             // Return possible completions in 'completions' array
173             completions.push_back( ... );
174         }
175
176         senf::term::LineEditor editor (...);
177         editor.defineKey(senf::term::KeyParser::TAB,
178                          boost::bind(&senf::term::bindings::complete, _1, &myCompleter));
179         \endcode
180
181         When \c myCompleter is a class member, use senf::membind() and pass this instead of \c
182         &myCompleter to \c boost::bind() and thus to senf::term::bindings::complete.
183
184         The completion protocol is as follows: When completion is desired, the completer function is
185         called. \a b and \a e are set to 0 and <tt>editor.point()</tt> respectively. \a prefix and
186         \a completions are empty.
187
188         \li the completer may restrict the to-be-completed string to any subrange by changing \a b
189             and \a e accordingly.
190         \li If there is an initial substring which applies to \e all completions but should not be
191             listed in the list of completions, assign this value to \a prefix.
192         \li Add all possible completions to the \a completions vector not including the \a prefix.
193         \li The completion result is taken from the size of the \a completions vector \e only: If
194             this vector is empty, completion failed (even if \a prefix is set), a single entry in \a
195             completions (even if it is the empty string) signals a unique completion.
196
197
198         \section editor_auxarea The aux display area
199
200         The auxiliary display area is accessed using auxDisplay() and clearAuxDisplay(). The aux
201         display area is \e cleared \e before each new key is processed. Therefore it is only
202         temporary. The aux display area however will survive hide() / show().
203
204
205         \section editor_hideshow Temporarily disabling the editor
206
207         Calling hide() will temporarily disable the editor. All editor display will be
208         removed. Calling show() will redisplay the editor in it's current state including the aux
209         display area.
210      */
211     class LineEditor
212         : public BaseEditor
213     {
214     public:
215         //-////////////////////////////////////////////////////////////////////////
216         // Types
217
218         typedef boost::function<void (LineEditor&)> KeyBinding; ///< Type of a key binding function
219         typedef boost::function<void (std::string const &)> AcceptCallback;
220                                         ///< Callback function type
221
222         static unsigned const MAX_HISTORY_SIZE = 1024u;
223
224         //-////////////////////////////////////////////////////////////////////////
225
226         LineEditor(AbstractTerminal & terminal, AcceptCallback cb);
227                                         ///< Create a LineEditor
228                                         /**< \param[in] terminal abstract terminal interface
229                                              \param[in] cb callback to call for complete input
230                                                  line */
231
232         //-////////////////////////////////////////////////////////////////////////
233
234         ///\name Overall edit control
235         //\{
236
237         void show();                    ///< Enable editor widget
238         void hide();                    ///< Disable editor widget
239         void accept();                  ///< Accept current user input and call the accept callback
240         void clear();                   ///< Clear editor buffer
241         void redisplay();               ///< Mark the editor buffer for redisplay
242         void forceRedisplay();          ///< Redisplay the editor buffer \e now
243         void prompt(std::string const & text); ///< Set prompt string
244
245         //\}
246
247         ///\name Cursor and display movement
248         //\{
249
250         void gotoChar(unsigned n);      ///< Move cursor to position \a n
251         void scrollTo(unsigned n);      ///< Move position \n to beginning of display line
252
253         //\}
254
255         ///\name Text manipulation
256         //\{
257
258         void deleteChar(unsigned n=1);  ///< Delete \a n characters at point
259         void insert(char ch);           ///< Insert \a ch at point
260         void insert(std::string const & text); ///< Insert \a text at point
261         void set(std::string const & text, unsigned pos = 0u);
262                                         ///< Set edit buffer contents
263                                         /**< The edit buffer contents will be replaced by \a
264                                              text. The cursor will be placed at position \a pos
265                                              within this text. */
266
267         //\}
268
269         ///\name History
270         //\{
271
272         void pushHistory(std::string const & text, bool accept = false);
273                                         ///< Add string \a text to history
274         void prevHistory();             ///< Switch to previous history entry
275         void nextHistory();             ///< Switch to next history entry
276
277         //\}
278
279         ///\name Aux Display
280         //\{
281
282         void auxDisplay(unsigned line, std::string const & text);
283                                         ///< Display \a text on aux display line \a line
284         unsigned maxAuxDisplayHeight(); ///< Get maximum height of the aux display area
285         void clearAuxDisplay();         ///< Clear the aux display area
286
287         //\}
288
289         ///\name Get information
290         //\{
291
292         std::string const & text();     ///< Get current editor buffer contents
293         unsigned point();               ///< Get current cursor position
294         unsigned displayPos();          ///< Get current display position
295         keycode_t lastKey();            ///< Get last key code received
296
297         //\}
298
299         ///\name Key bindings
300         //\{
301
302         void defineKey(keycode_t key, KeyBinding binding);
303                                         ///< Bind key \a key to \a binding
304         void unsetKey(keycode_t key);   ///< Remove all bindings for \a key
305
306         //\}
307
308     private:
309         virtual bool cb_init();
310         virtual void cb_windowSizeChanged();
311         virtual void v_keyReceived(keycode_t key);
312
313         typedef std::map<keycode_t, KeyBinding> KeyMap;
314         typedef std::vector<std::string> History;
315         typedef std::vector<std::string> AuxDisplay;
316
317         bool enabled_;
318         bool redisplayNeeded_;
319         std::string prompt_;
320         unsigned promptWidth_;
321         unsigned editWidth_;
322         std::string text_;
323         unsigned point_;
324         unsigned displayPos_;
325         keycode_t lastKey_;
326         AcceptCallback callback_;
327         KeyMap bindings_;
328         History history_;
329         unsigned historyPoint_;
330         AuxDisplay auxDisplay_;
331     };
332
333 /** \brief LineEditor key bindings
334  */
335 namespace bindings {
336
337     void selfInsertCommand   (LineEditor & editor); ///< Insert key as literal character
338     void forwardChar         (LineEditor & editor); ///< Move one char forward
339     void backwardChar        (LineEditor & editor); ///< Move one char backwards
340     void accept              (LineEditor & editor); ///< Accept input line
341     void acceptWithRepeat    (LineEditor & editor); ///< Accept, possibly repeat last history entry
342     void backwardDeleteChar  (LineEditor & editor); ///< Delete char before cursor
343     void deleteChar          (LineEditor & editor); ///< Delete char at cursor
344     void beginningOfLine     (LineEditor & editor); ///< Move to beginning of line
345     void endOfLine           (LineEditor & editor); ///< Move to end of line
346     void deleteToEndOfLine   (LineEditor & editor); ///< Delete from cursor to end of line
347     void restartEdit         (LineEditor & editor); ///< Clear edit buffer and restart edit
348     void prevHistory         (LineEditor & editor); ///< Move to previous history entry
349     void nextHistory         (LineEditor & editor); ///< Move to next history entry
350     void clearScreen         (LineEditor & editor); ///< Clear screen and redisplay editor
351
352     typedef boost::function<void (LineEditor &, unsigned & b, unsigned & e,
353                                   std::string & prefix, std::vector<std::string> &)> Completer;
354     void complete            (LineEditor & editor, Completer completer);
355                                         ///< Complete text at cursor
356                                         /**< This function calls \a completer to find the list of
357                                              possible completions for the text between \a b and \a e
358                                              (as passed to the completer). The completer must add
359                                              all possible completions to the \a completions vector.
360
361                                              \see \ref editor_complete */
362
363 }
364
365 }}
366
367 //-/////////////////////////////////////////////////////////////////////////////////////////////////
368 //#include "Editor.cci"
369 //#include "Editor.ct"
370 //#include "Editor.cti"
371 #endif
372
373 \f
374 // Local Variables:
375 // mode: c++
376 // fill-column: 100
377 // comment-column: 40
378 // c-file-style: "senf"
379 // indent-tabs-mode: nil
380 // ispell-local-dictionary: "american"
381 // compile-command: "scons -u test"
382 // End: