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

Last change on this file since 10493 was 10429, checked in by tbretz, 14 years ago
Moved the tools function into their own namespace to get rid of problems whenlinking with root.
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 = Tools::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.