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