a2221b821f66d11a03201a36d7e14cafcf8f3edc
[senf.git] / 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 ///////////////////////////////hh.p////////////////////////////////////////
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();               ///< Return current screen width
85         unsigned height();              ///< 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 suppoprt
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::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
185         \section editor_auxarea The aux display area
186
187         The auxiliary display area is accessed using auxDisplay() and clearAuxDisplay(). The aux
188         display area is \e cleared \e before each new key is processed. Therefore it is only
189         temporary. The aux display area however will survive hide() / show().
190
191
192         \section editor_hideshow Temporarily disabling the editor
193
194         Calling hide() will temporarily disable the editor. All editor display will be
195         removed. Calling show() will redisplay the editor in it's current state including the aux
196         display area.
197      */
198     class LineEditor
199         : public BaseEditor
200     {
201     public:
202         ///////////////////////////////////////////////////////////////////////////
203         // Types
204
205         typedef boost::function<void (LineEditor&)> KeyBinding; ///< Type of a key binding function
206         typedef boost::function<void (std::string const &)> AcceptCallback;
207                                         ///< Callback function type
208
209         static unsigned const MAX_HISTORY_SIZE = 1024u;
210
211         ///////////////////////////////////////////////////////////////////////////
212
213         LineEditor(AbstractTerminal & terminal, AcceptCallback cb);
214                                         ///< Create a LineEditor
215                                         /**< \parm[in] terminal abstract terminal interface
216                                              \parm[in] cb callback to call for complete input
217                                              line */
218         
219         ///////////////////////////////////////////////////////////////////////////
220
221         ///\name Overall edit control
222         ///\{
223
224         void show();                    ///< Enable editor widget
225         void hide();                    ///< Disable editor widget
226         void accept();                  ///< Accept current user input and call the accept callback
227         void clear();                   ///< Clear editor buffer
228         void redisplay();               ///< Mark the editor buffer for redisplay
229         void forceRedisplay();          ///< Redisplay the editor buffer \e now
230         void prompt(std::string const & text); ///< Set prompt string
231
232         ///\}
233
234         ///\name Cursor and display movement
235         ///\{
236
237         void gotoChar(unsigned n);      ///< Move cursor to position \a n
238         void scrollTo(unsigned n);      ///< Move positon \n to beginning of display line
239
240         ///\}
241
242         ///\name Text manipulation
243         ///\{
244
245         void deleteChar(unsigned n=1);  ///< Delete \a n characters at point
246         void insert(char ch);           ///< Insert \a ch at point
247         void insert(std::string const & text); ///< Insert \a text at point
248         void set(std::string const & text, unsigned pos = 0u);
249                                         ///< Set edit buffer contents
250                                         /**< The edit buffer contents will be replaced by \a
251                                              text. The cursor will be placed at position \a pos
252                                              within this text. */
253
254         ///\}
255
256         ///\name History
257         ///\{
258
259         void pushHistory(std::string const & text); ///< Add string \a text to history
260         void prevHistory();             ///< Switch to previous history entry
261         void nextHistory();             ///< Switch to next history entry
262
263         ///\}
264
265         ///\name Aux Display
266         ///\{
267
268         void auxDisplay(int line, std::string const & text);
269                                         ///< Display \a text on aux display line \a lilne
270         unsigned maxAuxDisplayHeight(); ///< Get maximum height of the aux display area
271         void clearAuxDisplay();         ///< Clear the aux display area
272
273         ///\}
274
275         ///\name Get information
276         ///\{
277
278         std::string const & text();     ///< Get current editor buffer contents
279         unsigned point();               ///< Get current cursor position
280         unsigned displayPos();          ///< Get current display position
281         keycode_t lastKey();            ///< Get last key code received
282
283         ///\}
284
285         ///\name Key bindings
286         ///\{
287
288         void defineKey(keycode_t key, KeyBinding binding);
289                                         ///< Bind key \a key to \a binding
290         void unsetKey(keycode_t key);   ///< Remove all bindings for \a key
291
292         ///\}
293         
294     private:
295         virtual bool cb_init();
296         virtual void cb_windowSizeChanged();
297         virtual void v_keyReceived(keycode_t key);
298
299         typedef std::map<keycode_t, KeyBinding> KeyMap;
300         typedef std::vector<std::string> History;
301         typedef std::vector<std::string> AuxDisplay;
302
303         bool enabled_;
304         bool redisplayNeeded_;
305         std::string prompt_;
306         unsigned promptWidth_;
307         unsigned editWidth_;
308         std::string text_;
309         unsigned point_;
310         unsigned displayPos_;
311         keycode_t lastKey_;
312         AcceptCallback callback_;
313         KeyMap bindings_;
314         History history_;
315         unsigned historyPoint_;
316         AuxDisplay auxDisplay_;
317     };
318
319 /** \brief LineEditor key bindings
320  */
321 namespace bindings {
322
323     void selfInsertCommand   (LineEditor & editor); ///< Insert key as literal character
324     void forwardChar         (LineEditor & editor); ///< Move one char forward
325     void backwardChar        (LineEditor & editor); ///< Move one char backwards
326     void accept              (LineEditor & editor); ///< Accept input line
327     void backwardDeleteChar  (LineEditor & editor); ///< Delete char before cursor
328     void deleteChar          (LineEditor & editor); ///< Delete char at cursor
329     void beginningOfLine     (LineEditor & editor); ///< Move to beginning of line
330     void endOfLine           (LineEditor & editor); ///< Move to end of line
331     void deleteToEndOfLine   (LineEditor & editor); ///< Delete from cursor to end of line
332     void restartEdit         (LineEditor & editor); ///< Clear edit buffer and restart edit
333     void prevHistory         (LineEditor & editor); ///< Move to previous history entry
334     void nextHistory         (LineEditor & editor); ///< Move to next history entry
335     void clearScreen         (LineEditor & editor); ///< Clear screen and redisplay editor
336
337     typedef boost::function<void (LineEditor &, unsigned b, unsigned e, std::vector<std::string> &)> Completer;
338     void complete            (LineEditor & editor, Completer completer);
339                                         ///< Complete text at cursor
340                                         /**< This function calls \a completer to find the list of
341                                              possible completions for the text between \a b and \a e
342                                              (as passed to the completer). The completer must add
343                                              all possible completions to the completions vector. 
344
345                                              \see \ref editor_complete */
346
347 }
348
349 }}
350
351 ///////////////////////////////hh.e////////////////////////////////////////
352 //#include "Editor.cci"
353 //#include "Editor.ct"
354 //#include "Editor.cti"
355 #endif
356
357 \f
358 // Local Variables:
359 // mode: c++
360 // fill-column: 100
361 // comment-column: 40
362 // c-file-style: "senf"
363 // indent-tabs-mode: nil
364 // ispell-local-dictionary: "american"
365 // compile-command: "scons -u test"
366 // End: