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