d0204329e8707c7921e02d4904a8223900965606
1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ncurses.h>
24 #include <glib.h>
26 #include "config.h"
27 #include "wreadln.h"
29 #define KEY_CTRL_A 1
30 #define KEY_CTRL_D 4
31 #define KEY_CTRL_E 5
32 #define KEY_CTRL_G 7
33 #define KEY_CTRL_K 11
34 #define KEY_BCKSPC 8
35 #define TAB 9
37 #define WRLN_MAX_LINE_SIZE 1024
38 #define WRLN_MAX_HISTORY_LENGTH 32
40 unsigned int wrln_max_line_size = WRLN_MAX_LINE_SIZE;
41 unsigned int wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
42 GVoidFunc wrln_resize_callback = NULL;
43 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
44 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
46 extern void screen_bell(void);
47 extern void sigstop(void);
49 char *
50 wreadln(WINDOW *w,
51 char *prompt,
52 char *initial_value,
53 int x1,
54 GList **history,
55 GCompletion *gcmp)
56 {
57 GList *hlist = NULL, *hcurrent = NULL;
58 char *line;
59 int x0, y, width;
60 int cursor = 0, start = 0;
61 int key = 0, i;
63 /* move the cursor one step to the right */
64 void cursor_move_right(void) {
65 if( cursor < strlen(line) && cursor<wrln_max_line_size-1 )
66 {
67 cursor++;
68 if( cursor+x0 >= x1 && start<cursor-width+1)
69 start++;
70 }
71 }
72 /* move the cursor one step to the left */
73 void cursor_move_left(void) {
74 if( cursor > 0 )
75 {
76 if( cursor==start && start > 0 )
77 start--;
78 cursor--;
79 }
80 }
81 /* move the cursor to the end of the line */
82 void cursor_move_to_eol(void) {
83 cursor = strlen(line);
84 if( cursor+x0 >= x1 )
85 start = cursor-width+1;
86 }
87 /* draw line buffer and update cursor position */
88 void drawline() {
89 wmove(w, y, x0);
90 /* clear input area */
91 whline(w, ' ', width);
92 /* print visible part of the line buffer */
93 waddnstr(w, line+start, width);
94 /* move the cursor to the correct position */
95 wmove(w, y, x0 + cursor-start);
96 /* tell ncurses to redraw the screen */
97 doupdate();
98 }
101 /* allocate a line buffer */
102 line = g_malloc0(wrln_max_line_size);
103 /* turn off echo */
104 noecho();
105 /* make shure the cursor is visible */
106 curs_set(1);
107 /* print prompt string */
108 if( prompt )
109 waddstr(w, prompt);
110 /* retrive y and x0 position */
111 getyx(w, y, x0);
112 /* check the x1 value */
113 if( x1<=x0 || x1>COLS )
114 x1 = COLS;
115 width = x1-x0;
116 /* clear input area */
117 mvwhline(w, y, x0, ' ', width);
119 if( history )
120 {
121 /* append the a new line to our history list */
122 *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
123 /* hlist points to the current item in the history list */
124 hlist = g_list_last(*history);
125 hcurrent = hlist;
126 }
128 if( initial_value == (char *) -1 )
129 {
130 /* get previous history entry */
131 if( history && hlist->prev )
132 {
133 if( hlist==hcurrent )
134 {
135 /* save the current line */
136 strncpy(hlist->data, line, wrln_max_line_size);
137 }
138 /* get previous line */
139 hlist = hlist->prev;
140 strncpy(line, hlist->data, wrln_max_line_size);
141 }
142 cursor_move_to_eol();
143 drawline();
144 }
145 else if( initial_value )
146 {
147 /* copy the initial value to the line buffer */
148 strncpy(line, initial_value, wrln_max_line_size);
149 cursor_move_to_eol();
150 drawline();
151 }
153 while( key!=13 && key!='\n' )
154 {
155 key = wgetch(w);
157 /* check if key is a function key */
158 for(i=0; i<63; i++)
159 if( key==KEY_F(i) )
160 {
161 key=KEY_F(1);
162 i=64;
163 }
165 switch (key)
166 {
167 #ifdef HAVE_GETMOUSE
168 case KEY_MOUSE: /* ignore mouse events */
169 #endif
170 case ERR: /* ingnore errors */
171 break;
173 #ifdef ENABLE_RAW_MODE
174 case 26:
175 sigstop();
176 break;
177 #endif
178 case KEY_RESIZE:
179 /* a resize event -> call an external callback function */
180 if( wrln_resize_callback )
181 wrln_resize_callback();
182 if( x1>COLS )
183 {
184 x1=COLS;
185 width = x1-x0;
186 cursor_move_to_eol();
187 }
188 /* make shure the cursor is visible */
189 curs_set(1);
190 break;
192 case TAB:
193 if( gcmp )
194 {
195 char *prefix = NULL;
196 GList *list;
198 if(wrln_pre_completion_callback)
199 wrln_pre_completion_callback(gcmp, line);
200 list = g_completion_complete(gcmp, line, &prefix);
201 if( prefix )
202 {
203 int len = strlen(prefix);
204 strncpy(line, prefix, len);
205 cursor_move_to_eol();
206 g_free(prefix);
207 }
208 else
209 screen_bell();
210 if( wrln_post_completion_callback )
211 wrln_post_completion_callback(gcmp, line, list);
212 }
213 break;
215 case KEY_CTRL_G:
216 screen_bell();
217 g_free(line);
218 if( history )
219 {
220 g_free(hcurrent->data);
221 hcurrent->data = NULL;
222 *history = g_list_delete_link(*history, hcurrent);
223 }
224 return NULL;
226 case KEY_LEFT:
227 cursor_move_left();
228 break;
229 case KEY_RIGHT:
230 cursor_move_right();
231 break;
232 case KEY_HOME:
233 case KEY_CTRL_A:
234 cursor = 0;
235 start = 0;
236 break;
237 case KEY_END:
238 case KEY_CTRL_E:
239 cursor_move_to_eol();
240 break;
241 case KEY_CTRL_K:
242 line[cursor] = 0;
243 break;
244 case 127:
245 case KEY_BCKSPC: /* handle backspace: copy all */
246 case KEY_BACKSPACE: /* chars starting from curpos */
247 if( cursor > 0 ) /* - 1 from buf[n+1] to buf */
248 {
249 for (i = cursor - 1; line[i] != 0; i++)
250 line[i] = line[i + 1];
251 cursor_move_left();
252 }
253 break;
254 case KEY_DC: /* handle delete key. As above */
255 case KEY_CTRL_D:
256 if( cursor <= strlen(line) - 1 )
257 {
258 for (i = cursor; line[i] != 0; i++)
259 line[i] = line[i + 1];
260 }
261 break;
262 case KEY_UP:
263 /* get previous history entry */
264 if( history && hlist->prev )
265 {
266 if( hlist==hcurrent )
267 {
268 /* save the current line */
269 strncpy(hlist->data, line, wrln_max_line_size);
270 }
271 /* get previous line */
272 hlist = hlist->prev;
273 strncpy(line, hlist->data, wrln_max_line_size);
274 }
275 // if (cursor > strlen(line))
276 cursor_move_to_eol();
277 break;
278 case KEY_DOWN:
279 /* get next history entry */
280 if( history && hlist->next )
281 {
282 /* get next line */
283 hlist = hlist->next;
284 strncpy(line, hlist->data, wrln_max_line_size);
285 }
286 cursor_move_to_eol();
287 break;
289 case '\n':
290 case 13:
291 case KEY_IC:
292 case KEY_PPAGE:
293 case KEY_NPAGE:
294 case KEY_F(1):
295 /* ignore char */
296 break;
297 default:
298 if (key >= 32)
299 {
300 if (strlen (line + cursor)) /* if the cursor is */
301 { /* not at the last pos */
302 char *tmp = 0;
303 tmp = g_malloc0(strlen (line + cursor) + 1);
304 strcpy (tmp, line + cursor);
305 line[cursor] = key;
306 line[cursor + 1] = 0;
307 strcat (&line[cursor + 1], tmp);
308 g_free(tmp);
309 cursor_move_right();
310 }
311 else
312 {
313 line[cursor + 1] = 0;
314 line[cursor] = key;
315 cursor_move_right();
316 }
317 }
318 }
320 drawline();
321 }
323 /* update history */
324 if( history )
325 {
326 if( strlen(line) )
327 {
328 /* update the current history entry */
329 size_t size = strlen(line)+1;
330 hcurrent->data = g_realloc(hcurrent->data, size);
331 strncpy(hcurrent->data, line, size);
332 }
333 else
334 {
335 /* the line was empty - remove the current history entry */
336 g_free(hcurrent->data);
337 hcurrent->data = NULL;
338 *history = g_list_delete_link(*history, hcurrent);
339 }
341 while( g_list_length(*history) > wrln_max_history_length )
342 {
343 GList *first = g_list_first(*history);
345 /* remove the oldest history entry */
346 g_free(first->data);
347 first->data = NULL;
348 *history = g_list_delete_link(*history, first);
349 }
350 }
352 return g_realloc(line, strlen(line)+1);
353 }