Update copyright year.
[cboard.git] / src / input.c
blobcb93d945a8ba8d8d57a4473e6339c230c3e9a5b9
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2024 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 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
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
29 #ifdef HAVE_STDARG_H
30 #include <stdarg.h>
31 #endif
33 #include "common.h"
34 #include "conf.h"
35 #include "colors.h"
36 #include "window.h"
37 #include "misc.h"
38 #include "message.h"
39 #include "input.h"
40 #include "common.h"
41 #include "keys.h"
42 #include "rcfile.h"
44 static struct input_history_s
46 char *str;
47 struct input_history_s *next;
48 struct input_history_s *prev;
49 struct input_history_s *head;
50 } *input_history[INPUT_HIST_MAX];
52 static void
53 add_input_history (int which, const char *str)
55 struct input_history_s *new;
56 struct input_history_s *p = NULL;
58 if (!str || !*str || which < 0)
59 return;
61 if (input_history[which])
62 for (p = input_history[which]->head; p->next; p = p->next);
64 new = Calloc (1, sizeof (struct input_history_s));
65 if (!new)
67 fprintf (stderr, "Out of core!\n");
68 exit (EXIT_FAILURE);
71 new->str = strdup (str);
72 new->prev = p;
74 if (p)
76 p->next = new;
77 new->head = p->head;
79 else
80 new->head = new;
82 input_history[which] = p ? p->next : new;
85 static bool
86 validate_pgn_tag_name (int c, const void *arg)
88 if (!isalnum (c) && c != '_')
89 return FALSE;
91 return TRUE;
94 static bool
95 validate_pgn_date (int c, const void *arg)
97 if (!isdigit (c) && c != '.' && c != '?')
98 return FALSE;
100 return TRUE;
103 static bool
104 validate_pgn_round (int c, const void *arg)
106 if (!isdigit (c) && c != '.' && c != '-' && c != '?')
107 return FALSE;
109 return TRUE;
112 static bool
113 validate_pgn_result (int c, const void *arg)
115 if (c != '1' && c != '0' && c != '2' && c != '*' && c != '/' && c != '-')
116 return FALSE;
118 return TRUE;
121 static int
122 get_input (WIN * win)
124 struct input_s *in = win->data;
125 char *tmp;
126 struct input_data_s *data = in->data;
127 int i, n;
128 char *prompt = _("Type %ls for help");
129 char buf[255];
130 char str[MB_CUR_MAX];
132 snprintf (buf, sizeof (buf), prompt, key_lookup (global_keys, do_global_help));
133 curs_set (1);
134 window_draw_title (win->w, win->title, in->w, CP_INPUT_TITLE,
135 CP_INPUT_BORDER);
137 if (in->extra)
139 for (i = 0; in->extra[i]; i++)
140 window_draw_prompt (win->w, in->lines + 2 + i, in->w, in->extra[i],
141 CP_INPUT_PROMPT);
143 window_draw_prompt (win->w, in->lines + 2 + i, in->w, buf,
144 CP_INPUT_PROMPT);
146 else
147 window_draw_prompt (win->w, in->lines + 2, in->w, buf, CP_INPUT_PROMPT);
149 if (in->func && in->c && win->c == in->c)
151 (*in->func) (in);
152 return 1;
155 if (win->c == keycode_lookup (global_keys, do_global_help))
157 message (_("Line Editing Keys"), ANY_KEY_STR,
158 "%s",
159 _("UP/DOWN/LEFT/RIGHT - position cursor (multiline)\n"
160 " UP/CTRL-P - previous input history\n"
161 " DOWN/CTRL-N - next input history\n"
162 " HOME/CTRL-A - move cursor to the beginning of line\n"
163 " END/CTRL-E - move cursor to the end of line\n"
164 " CTRL-B/W - move cursor to previous/next word\n"
165 " CTRL-X - delete word under cursor\n"
166 " CTRL-K - delete from cursor to end of line\n"
167 " CTRL-U - clear entire input field\n"
168 " BACKSPACE - delete previous character\n"
169 " ESCAPE - quit without changes\n"
170 " ENTER - quit with changes"));
171 goto ok;
174 switch (win->c)
176 case CTRL_KEY ('X'):
177 form_driver (in->f, REQ_DEL_WORD);
178 break;
179 case CTRL_KEY ('B'):
180 form_driver (in->f, REQ_PREV_WORD);
181 break;
182 case CTRL_KEY ('W'):
183 form_driver (in->f, REQ_NEXT_WORD);
184 break;
185 case KEY_HOME:
186 case CTRL_KEY ('A'):
187 form_driver (in->f, REQ_BEG_LINE);
188 break;
189 case KEY_END:
190 case CTRL_KEY ('E'):
191 form_driver (in->f, REQ_END_LINE);
192 break;
193 case CTRL_KEY ('K'):
194 form_driver (in->f, REQ_CLR_EOL);
195 break;
196 case CTRL_KEY ('U'):
197 form_driver (in->f, REQ_CLR_FIELD);
198 break;
199 case KEY_LEFT:
200 form_driver (in->f, REQ_LEFT_CHAR);
201 break;
202 case KEY_RIGHT:
203 form_driver (in->f, REQ_RIGHT_CHAR);
204 break;
205 case CTRL_KEY ('P'):
206 case KEY_UP:
207 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1)
209 set_field_buffer (in->fields[0], 0, input_history[in->hist]->str);
210 form_driver (in->f, REQ_END_LINE);
212 if (!input_history[in->hist]->prev)
213 input_history[in->hist] = input_history[in->hist]->head;
214 else
215 input_history[in->hist] = input_history[in->hist]->prev;
217 break;
220 form_driver (in->f, REQ_UP_CHAR);
221 break;
222 case CTRL_KEY ('N'):
223 case KEY_DOWN:
224 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1)
226 if (!input_history[in->hist]->next)
228 set_field_buffer (in->fields[0], 0, NULL);
229 form_driver (in->f, REQ_CLR_FIELD);
230 break;
233 input_history[in->hist] = input_history[in->hist]->next;
234 set_field_buffer (in->fields[0], 0, input_history[in->hist]->str);
235 form_driver (in->f, REQ_END_LINE);
236 break;
239 form_driver (in->f, REQ_DOWN_CHAR);
240 break;
241 case '\010':
242 case KEY_BACKSPACE:
243 form_driver (in->f, REQ_DEL_PREV);
244 break;
245 case '\n':
246 tmp = field_buffer (in->fields[0], 0);
247 goto done;
248 case KEY_ESCAPE:
249 if (in->reset)
250 tmp = NULL;
251 else
252 tmp = (in->buf[0]) ? in->buf : NULL;
253 goto done;
254 default:
255 n = wctomb (str, win->c);
256 for (i = 0; n != -1 && i < n; i++)
257 form_driver (in->f, (unsigned char) str[i]);
258 break;
262 form_driver (in->f, REQ_VALIDATION);
263 return 1;
265 done:
266 if (tmp)
268 tmp = trim (tmp);
270 if (tmp[0])
272 strncpy (in->buf, tmp, sizeof (in->buf) - 1);
273 in->buf[sizeof (in->buf) - 1] = 0;
275 else
276 in->buf[0] = 0;
278 else
279 in->buf[0] = 0;
281 unpost_form (in->f);
282 free_form (in->f);
283 free_field (in->fields[0]);
285 if (in->extra)
287 for (i = 0; in->extra[i]; i++)
288 free (in->extra[i]);
290 free (in->extra);
293 data->str = (in->buf[0]) ? strdup (in->buf) : NULL;
295 if (in->lines == 1)
296 add_input_history (in->hist, in->buf);
298 win->data = data;
299 free_fieldtype (in->ft);
300 free (in);
301 curs_set (0);
302 return 0;
305 static void
306 input_resize_func (WIN *w)
308 move_panel (w->p, CALCPOSY (w->rows), CALCPOSX (w->cols));
312 * This function prompts for input. The init argument is the initial value.
313 * The lines argument is how many lines the field is. If zero, then it is
314 * dynamically determined based on the init argument or INPUT_WIDTH if init is
315 * NULL.
317 * The reset argument is whether pressing ESC returns the initial value or
318 * NULL.
320 * The extra_help argument is an extra line of help prompt normally used with
321 * the custom_func argument. The custom_func argument is a pointer to a
322 * function of type void which takes one pointer-to-void argument. This
323 * function is called when the ckey argument is pressed.
325 * The efunc parameter is the function that is ran when the window is done
326 * getting input or NULL. This function is ran just before the window is
327 * destroyed.
329 * The type argument is the type of validation for the input defined in
330 * common.h. Remaining arguments are values for the type argument. See
331 * field_type(3X) for validation types.
333 * FIXME form validation is buggy (integers).
335 WIN *
336 construct_input (const char *title, const char *init, int lines, int reset,
337 const char *extra_help, input_func * func, void *arg,
338 wint_t key, struct input_data_s * id, int history,
339 window_resize_func rfunc, int type, ...)
341 WIN *win;
342 struct input_s *in;
343 int l = (lines > 0) ? lines : 1;
344 va_list ap;
345 FIELDTYPE *ft = NULL;
346 int eh = 0;
348 l += 2;
349 in = Calloc (1, sizeof (struct input_s));
350 in->w = INPUT_WIDTH;
352 if (extra_help)
354 char *tmp = strdup (extra_help);
356 in->extra = split_str (tmp, "\n", &eh, &in->w, 0);
357 l += eh;
358 free (tmp);
361 if (title)
362 l++;
364 in->h = l + 1;
365 in->func = func;
366 in->arg = arg;
367 in->c = key;
368 in->lines = (lines) ? lines : 1;
369 win = window_create (title, in->h, in->w, CALCPOSY (in->h), CALCPOSX (in->w),
370 get_input, in, id->efunc,
371 rfunc ? rfunc : input_resize_func);
372 in = win->data;
373 in->hist = history;
374 in->data = id;
375 in->reset = reset;
376 in->fields[0] = new_field (in->lines, in->w - 2, 0, 0, 0, 0);
377 va_start (ap, type);
379 switch (type)
381 case FIELD_TYPE_PGN_ROUND:
382 ft = new_fieldtype (NULL, validate_pgn_round);
383 set_field_type (in->fields[0], ft);
384 break;
385 case FIELD_TYPE_PGN_RESULT:
386 ft = new_fieldtype (NULL, validate_pgn_result);
387 set_field_type (in->fields[0], ft);
388 break;
389 case FIELD_TYPE_PGN_DATE:
390 ft = new_fieldtype (NULL, validate_pgn_date);
391 set_field_type (in->fields[0], ft);
392 break;
393 case FIELD_TYPE_PGN_TAG_NAME:
394 ft = new_fieldtype (NULL, validate_pgn_tag_name);
395 set_field_type (in->fields[0], ft);
396 break;
397 case FIELD_TYPE_ALNUM:
398 set_field_type (in->fields[0], TYPE_ALNUM, va_arg (ap, int));
399 break;
400 case FIELD_TYPE_ALPHA:
401 set_field_type (in->fields[0], TYPE_ALPHA, va_arg (ap, int));
402 break;
403 case FIELD_TYPE_ENUM:
404 set_field_type (in->fields[0], TYPE_ENUM, va_arg (ap, char **),
405 va_arg (ap, int), va_arg (ap, int));
406 break;
407 case FIELD_TYPE_INTEGER:
408 set_field_type (in->fields[0], TYPE_INTEGER, va_arg (ap, int),
409 va_arg (ap, long), va_arg (ap, long));
410 break;
411 case FIELD_TYPE_NUMERIC:
412 set_field_type (in->fields[0], TYPE_NUMERIC, va_arg (ap, int),
413 va_arg (ap, double), va_arg (ap, double));
414 break;
415 case FIELD_TYPE_REGEXP:
416 set_field_type (in->fields[0], TYPE_REGEXP, va_arg (ap, char *));
417 break;
418 /* Solaris 5.9 */
419 #ifdef HAVE_TYPE_IPV4
420 case FIELD_TYPE_IPV4:
421 set_field_type (in->fields[0], TYPE_IPV4);
422 break;
423 #endif
424 default:
425 set_field_type (in->fields[0], NULL);
426 break;
429 va_end (ap);
431 if (init)
433 strncpy (in->buf, init, sizeof (in->buf) - 1);
434 in->buf[sizeof (in->buf) - 1] = 0;
435 set_field_buffer (in->fields[0], 0, init);
438 in->ft = ft;
440 if (in->lines == 1)
441 field_opts_off (in->fields[0], O_STATIC);
443 if (in->buf[0])
444 set_field_buffer (in->fields[0], 0, in->buf);
446 field_opts_off (in->fields[0], O_BLANK | O_AUTOSKIP);
447 in->fields[1] = NULL;
448 in->f = new_form (in->fields);
449 form_opts_off (in->f, O_BS_OVERLOAD);
450 set_form_win (in->f, win->w);
451 in->sw = derwin (win->w, in->lines, in->w - 2, 2, 1);
452 set_form_sub (in->f, in->sw);
453 post_form (in->f);
454 form_driver (in->f, REQ_END_FIELD);
455 keypad (win->w, TRUE);
456 curs_set (1);
457 wbkgd (win->w, CP_INPUT_WINDOW);
458 (*win->func) (win);
459 return win;