source: trunk/FACT++/src/ReadlineWindow.cc @ 10183

Last change on this file since 10183 was 10183, checked in by tbretz, 9 years ago
New import.
File size: 10.4 KB
Line 
1// **************************************************************************
2/** @class ReadlineWindow
3
4@brief Helper to redirect readline's in- and output to an ncurses window
5
6The idea of this class is to allow the use of the readline functionality
7within a ncurses window. Therefore, several callback and hook function
8of the readline libarary are redirected, which allow to redirect the
9window's input stream to readline and the readline output to the
10window.
11
12After instantisation a pointer to a ncurses WINDOW must be given to the
13object to 'bind' readline to this window.
14
15In addition the color of the readline prompt can be set using the
16SetPromptColor() member function. Note that the given color must
17be the index of a COLOR_PAIR. For details on this see the ncurses
18documentation
19
20If ncurses will use more than one window on the screen it might
21be necessary to redraw the screen before the cursor is displayed.
22Therefore, the Refresh() function must be overwritten. It is called
23before the cursor is put to its final location in the readline line
24and after all update to the screen was performed.
25
26Refresh() can be used to force a redisplay of the current input line
27from a derived class. This might be necessary after changes top the
28screen or window size.
29
30@section References
31
32 - <A HREF="http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html">GNU Readline</A>
33 - <A HREF="http://www.gnu.org/software/ncurses">GNU Ncurses</A>
34
35**/
36// **************************************************************************
37#include "Shell.h"
38
39#include <iostream>
40#include <string.h> // strlen
41
42#include "tools.h"
43
44using namespace std;
45
46// --------------------------------------------------------------------------
47//
48//! Propagate prgname to the Readline constructor.
49//! Initialize fPromtX and fPromtY to 0.
50//! Set fWindow to 0.
51//!
52//! @param prgname
53//!    see Readline::Readline()
54//!
55//! @todo
56//!    Maybe we should add sanity check for fWindow==0 in the functions?
57//!
58ReadlineWindow::ReadlineWindow(const char *prgname) : Readline(prgname),
59    fWindow(0), fPromptX(0), fPromptY(0)
60{
61}
62
63// --------------------------------------------------------------------------
64//
65//! Set the window in which the readline prompt and all readline output
66//! is displayed. Sets the readline screen size (rl_set_screen_size)
67//! accoring to the size of the window.
68//!
69//! Setup all necessry callback functions. To redirect readline input
70//! and output properly.
71//!
72//! @param w
73//!    Pointer to a ncurses WINDOW.
74//
75void ReadlineWindow::SetWindow(WINDOW *w)
76{
77    if (!w)
78        return;
79
80    // Get size of the window
81    int width, height;
82    getmaxyx(w, height, width);
83
84    // Propagate the size to the readline library
85    Resize(width, height);
86
87    // Finally set the pointer to the panel in which we are supposed to
88    // operate
89    fWindow = w;
90}
91
92
93// --------------------------------------------------------------------------
94//
95//! Callback function for readlines rl_getc_function. Apart from redirecting
96//! the input from the window to the readline libarary, it also checks if
97//! the input forced scrolling of the window contents and in this case
98//! adapt fPromptY.
99//!
100//! @return
101//!    the character read by wgetch
102//
103/*
104int ReadlineWindow::Getc(FILE *)
105{
106    /// ====   It seems this is obsolete because we will get teh scrolling
107    //         from the continous call to event hook anyway
108
109    // Get size of the window
110    int lines, cols;
111    getmaxyx(fWindow, lines, cols);
112
113    // Get current cursor position
114    int x0, y0, y1, x1;
115    getyx(fWindow, y0, x0);
116
117    // Read a character from stream in window
118    const int c = wgetch(fWindow);
119
120    // Get new cursor position
121    getyx(fWindow, y1, x1);
122
123    // Find out whether the last character initiated a scroll
124    if (y0==lines-1 && y1==lines-1 && x1==0 && x0==cols-1)
125        fPromptY--;
126
127    // return character
128    return c;
129}
130*/
131
132// --------------------------------------------------------------------------
133//
134//! Store the current cursor position in fPromptX/Y
135//
136void ReadlineWindow::Startup()
137{
138    getyx(fWindow, fPromptY, fPromptX);
139}
140
141// --------------------------------------------------------------------------
142//
143//! Move the cursor to the position stored in fPromptX/Y which should
144//! correspond to the beginning of the output line
145//
146void ReadlineWindow::RewindCursor() const
147{
148    wmove(fWindow, fPromptY, fPromptX);
149}
150
151// --------------------------------------------------------------------------
152//
153//! The event hook which is called regularly when a readline call is in
154//! progress. We use this to synchronously upadte our prompt (mainly
155//! the current cursor position) and refresh the screen, so that all
156//! changes get displayed soon.
157//!
158//! By default, this will be called at most ten times a second if there
159//! is no keyboard input.
160//
161void ReadlineWindow::EventHook()
162{
163    Readline::EventHook();
164    Redisplay();
165    /*
166     * This doesn't work if the contents of the line changes, e.g. when
167     * the prompt is replaced
168
169    // Refresh the screen
170    Refresh();
171
172    // Now move the cursor to its expected position
173    int lines, cols;
174    getmaxyx(fWindow, lines, cols);
175
176    const int pos = fPromptY*cols + fPromptX + GetAbsCursor();
177    wmove(fWindow, pos/cols, pos%cols);
178
179    // Make the cursor movement visible on the screen
180    wrefresh(fWindow);
181    */
182
183    // The lines above are just a simplified version of Redisplay()
184    // which skips all the output.
185}
186
187// --------------------------------------------------------------------------
188//
189//! This basically implement displaying the whole line, starting with the
190//! prompt and the rl_line_buffer. It also checks if displaying it
191//! results in a scroll of the window and adapt fPromptY accordingly.
192//!
193//! The prompt is displayed in the color defined by fColor.
194//!
195//! Before the cursor position is finally set a screen refresh (Refresh())
196//! is initiated to ensure that nothing afterwards will change the cursor
197//! position. It might be necessary to overwrite this function.
198//!
199//! @todo fix docu
200//
201void ReadlineWindow::Redisplay()
202{
203    // Move to the beginning of the output
204    wmove(fWindow, fPromptY, fPromptX);
205
206    // Get site of the window
207    int lines, cols;
208    getmaxyx(fWindow, lines, cols);
209
210    const string prompt = GetPrompt();
211    const string buffer = GetBuffer();
212
213    // Issue promt and redisplay text
214    wattron(fWindow, fColor);
215    wprintw(fWindow, "%s", prompt.c_str());
216    wattroff(fWindow, fColor);
217    wprintw(fWindow, "%s", buffer.c_str());
218
219    // Clear everything after that
220    wclrtobot(fWindow);
221
222    // Calculate absolute position in window or beginning of output
223    int xy = fPromptY*cols + fPromptX;
224
225    // Calculate position of end of prompt
226    xy += prompt.length();
227
228    // Calculate position of cursor and end of output
229    const int cur = xy + GetCursor();
230    const int end = xy + buffer.size();
231
232    // Calculate if the above output scrolled the window and by how many lines
233    int scrolls = end/cols - lines + 1;
234
235    if (scrolls<0)
236        scrolls = 0;
237
238    fPromptY -= scrolls;
239
240    // new position
241    const int px = cur%cols;
242    const int py = scrolls>=1 ? cur/cols - scrolls : cur/cols;
243
244    // Make sure that whatever happens while typing the correct
245    // screen is shown (otherwise the top-panel disappears when
246    // we scroll)
247    Refresh();
248
249    // Move the cursor to the cursor position
250    wmove(fWindow, py, px);
251
252    // Make changes visible on screen
253    wrefresh(fWindow);
254}
255
256// --------------------------------------------------------------------------
257//
258//! Callback function to display the finally compiled list of completion
259//! options. Adapts fPromtY for the number of scrolled lines.
260//!
261//! @param matches
262//!    A list with the matches found. The first element contains
263//!    what was completed. The length of the list is therefore
264//!    num+1.
265//!
266//! @param num
267//!    Number of possible completion entries in the list.
268//!
269//! @param max
270//!    maximum length of the entries
271//!
272//! @todo
273//!    Maybe we can use rl_outstream here if we find a way to redirect
274//!    the stream to us instead of a file.
275//
276void ReadlineWindow::CompletionDisplay(char **matches, int num, int max)
277{
278    // Increase maximum size by two to get gaps in between the columns
279    max += 2; // Two whitespaces in between the output
280
281    // Get size of window
282    int lines, cols;
283    getmaxyx(fWindow, lines, cols);
284
285    // Allow an empty space at the end of the lines for a '\n'
286    cols--;
287
288    // calculate the final number columns
289    const int ncols = cols / max;
290
291    // Compile a proper format string
292    const string fmt = Form("%%-%ds", max);
293
294    // loop over all entries and display them
295    int l=0;
296    for (int i=0; i<num; i++)
297    {
298        // Check if we have to put a line-break
299        if (i%ncols==0)
300        {
301            if ((max+0)*ncols < cols)
302                wprintw(fWindow, "\n");
303            l++;
304        }
305
306        // Display an entry
307        wprintw(fWindow, fmt.c_str(), matches[i+1]);
308    }
309
310    // Display an empty line after the list
311    if ((num-1)%ncols>0)
312        wprintw(fWindow, "\n");
313    wprintw(fWindow, "\n");
314
315    // Get new cursor position
316    int x, y;
317    getyx(fWindow, y, x);
318
319    // Clear everything behind the list
320    wclrtobot(fWindow);
321
322    // Adapt fPromptY for the number of scrolled lines if any.
323    if (y==lines-1)
324        fPromptY = lines-1;
325
326    // Display anything
327    wrefresh(fWindow);
328}
329
330// --------------------------------------------------------------------------
331//
332//! Overwrites Shutdown() of Readline. After Readline::Prompt has
333//! returned a Redisplay() is forced to ensure a proper display of
334//! everything. Finally, the display line is ended by a \n.
335//!
336//! @param buf
337//!    The buffer returned by the readline call
338//
339void ReadlineWindow::Shutdown(const char *buf)
340{
341    // Move the cursor to the end of the total line entered by the user
342    // (the user might have pressed return in the middle of the line)...
343    int lines, cols;
344    getmaxyx(fWindow, lines, cols);
345
346    // Calculate absolute position in window or beginning of output
347    // We can't take a pointer to the buffer because rl_end is not
348    // valid anymore at the end of a readline call
349    int xy = fPromptY*cols + fPromptX + GetPrompt().size() + strlen(buf);
350    wmove(fWindow, xy/cols, xy%cols);
351
352    // ...and output a newline. We have to do the manually.
353    wprintw(fWindow, "\n");
354
355    // refresh the screen
356    wrefresh(fWindow);
357
358    // This might have scrolled the window
359    if (xy/cols==lines-1)
360        fPromptY--;
361}
Note: See TracBrowser for help on using the repository browser.