Update URL's to GitLab.
[cboard.git] / src / input.c
blob71dacbf5f2832761456405633038c92742588cfb
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2018 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"
42 static struct input_history_s
44 char *str;
45 struct input_history_s *next;
46 struct input_history_s *prev;
47 struct input_history_s *head;
48 } *input_history[INPUT_HIST_MAX];
50 static void
51 add_input_history (int which, const char *str)
53 struct input_history_s *new;
54 struct input_history_s *p = NULL;
56 if (!str || !*str || which < 0)
57 return;
59 if (input_history[which])
60 for (p = input_history[which]->head; p->next; p = p->next);
62 new = calloc (1, sizeof (struct input_history_s));
63 if (!new)
65 fprintf (stderr, "Out of core!\n");
66 exit (EXIT_FAILURE);
69 new->str = strdup (str);
70 new->prev = p;
72 if (p)
74 p->next = new;
75 new->head = p->head;
77 else
78 new->head = new;
80 input_history[which] = p ? p->next : new;
83 static bool
84 validate_pgn_tag_name (int c, const void *arg)
86 if (!isalnum (c) && c != '_')
87 return FALSE;
89 return TRUE;
92 static bool
93 validate_pgn_date (int c, const void *arg)
95 if (!isdigit (c) && c != '.' && c != '?')
96 return FALSE;
98 return TRUE;
101 static bool
102 validate_pgn_round (int c, const void *arg)
104 if (!isdigit (c) && c != '.' && c != '-' && c != '?')
105 return FALSE;
107 return TRUE;
110 static bool
111 validate_pgn_result (int c, const void *arg)
113 if (c != '1' && c != '0' && c != '2' && c != '*' && c != '/' && c != '-')
114 return FALSE;
116 return TRUE;
119 static int
120 get_input (WIN * win)
122 struct input_s *in = win->data;
123 char *tmp;
124 struct input_data_s *data = in->data;
125 int i, n;
126 char *prompt = _("Type F1 for help");
127 char str[MB_CUR_MAX];
129 curs_set (1);
130 window_draw_title (win->w, win->title, in->w, CP_INPUT_TITLE,
131 CP_INPUT_BORDER);
133 if (in->extra)
135 for (i = 0; in->extra[i]; i++)
136 window_draw_prompt (win->w, in->lines + 2 + i, in->w, in->extra[i],
137 CP_INPUT_PROMPT);
139 window_draw_prompt (win->w, in->lines + 2 + i, in->w, prompt,
140 CP_INPUT_PROMPT);
142 else
143 window_draw_prompt (win->w, in->lines + 2, in->w, prompt,
144 CP_INPUT_PROMPT);
146 if (in->func && in->c && win->c == in->c)
148 (*in->func) (in);
149 return 1;
152 switch (win->c)
154 case CTRL_KEY ('X'):
155 form_driver (in->f, REQ_DEL_WORD);
156 break;
157 case CTRL_KEY ('B'):
158 form_driver (in->f, REQ_PREV_WORD);
159 break;
160 case CTRL_KEY ('W'):
161 form_driver (in->f, REQ_NEXT_WORD);
162 break;
163 case KEY_HOME:
164 case CTRL_KEY ('A'):
165 form_driver (in->f, REQ_BEG_LINE);
166 break;
167 case KEY_END:
168 case CTRL_KEY ('E'):
169 form_driver (in->f, REQ_END_LINE);
170 break;
171 case CTRL_KEY ('K'):
172 form_driver (in->f, REQ_CLR_EOL);
173 break;
174 case CTRL_KEY ('U'):
175 form_driver (in->f, REQ_CLR_FIELD);
176 break;
177 case KEY_F (1):
178 message (_("Line Editing Keys"), ANY_KEY_STR,
179 "%s",
180 _("UP/DOWN/LEFT/RIGHT - position cursor (multiline)\n"
181 " UP/CTRL-P - previous input history\n"
182 " DOWN/CTRL-N - next input history\n"
183 " HOME/CTRL-A - move cursor to the beginning of line\n"
184 " END/CTRL-E - move cursor to the end of line\n"
185 " CTRL-B/W - move cursor to previous/next word\n"
186 " CTRL-X - delete word under cursor\n"
187 " CTRL-K - delete from cursor to end of line\n"
188 " CTRL-U - clear entire input field\n"
189 " BACKSPACE - delete previous character\n"
190 " ESCAPE - quit without changes\n"
191 " ENTER - quit with changes"));
192 break;
193 case KEY_LEFT:
194 form_driver (in->f, REQ_LEFT_CHAR);
195 break;
196 case KEY_RIGHT:
197 form_driver (in->f, REQ_RIGHT_CHAR);
198 break;
199 case CTRL_KEY ('P'):
200 case KEY_UP:
201 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1)
203 set_field_buffer (in->fields[0], 0, input_history[in->hist]->str);
204 form_driver (in->f, REQ_END_LINE);
206 if (!input_history[in->hist]->prev)
207 input_history[in->hist] = input_history[in->hist]->head;
208 else
209 input_history[in->hist] = input_history[in->hist]->prev;
211 break;
214 form_driver (in->f, REQ_UP_CHAR);
215 break;
216 case CTRL_KEY ('N'):
217 case KEY_DOWN:
218 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1)
220 if (!input_history[in->hist]->next)
222 set_field_buffer (in->fields[0], 0, NULL);
223 form_driver (in->f, REQ_CLR_FIELD);
224 break;
227 input_history[in->hist] = input_history[in->hist]->next;
228 set_field_buffer (in->fields[0], 0, input_history[in->hist]->str);
229 form_driver (in->f, REQ_END_LINE);
230 break;
233 form_driver (in->f, REQ_DOWN_CHAR);
234 break;
235 case '\010':
236 case KEY_BACKSPACE:
237 form_driver (in->f, REQ_DEL_PREV);
238 break;
239 case '\n':
240 tmp = field_buffer (in->fields[0], 0);
241 goto done;
242 case KEY_ESCAPE:
243 if (in->reset)
244 tmp = NULL;
245 else
246 tmp = (in->buf[0]) ? in->buf : NULL;
247 goto done;
248 default:
249 n = wctomb (str, win->c);
250 for (i = 0; n != -1 && i < n; i++)
251 form_driver (in->f, (unsigned char) str[i]);
252 break;
255 form_driver (in->f, REQ_VALIDATION);
256 return 1;
258 done:
259 if (tmp)
261 tmp = trim (tmp);
263 if (tmp[0])
265 strncpy (in->buf, tmp, sizeof (in->buf) - 1);
266 in->buf[sizeof (in->buf) - 1] = 0;
268 else
269 in->buf[0] = 0;
271 else
272 in->buf[0] = 0;
274 unpost_form (in->f);
275 free_form (in->f);
276 free_field (in->fields[0]);
278 if (in->extra)
280 for (i = 0; in->extra[i]; i++)
281 free (in->extra[i]);
283 free (in->extra);
286 data->str = (in->buf[0]) ? strdup (in->buf) : NULL;
288 if (in->lines == 1)
289 add_input_history (in->hist, in->buf);
291 win->data = data;
292 free_fieldtype (in->ft);
293 free (in);
294 curs_set (0);
295 return 0;
298 static void
299 input_resize_func (WIN *w)
301 move_panel (w->p, CALCPOSY (w->rows), CALCPOSX (w->cols));
305 * This function prompts for input. The init argument is the initial value.
306 * The lines argument is how many lines the field is. If zero, then it is
307 * dynamically determined based on the init argument or INPUT_WIDTH if init is
308 * NULL.
310 * The reset argument is whether pressing ESC returns the initial value or
311 * NULL.
313 * The extra_help argument is an extra line of help prompt normally used with
314 * the custom_func argument. The custom_func argument is a pointer to a
315 * function of type void which takes one pointer-to-void argument. This
316 * function is called when the ckey argument is pressed.
318 * The efunc parameter is the function that is ran when the window is done
319 * getting input or NULL. This function is ran just before the window is
320 * destroyed.
322 * The type argument is the type of validation for the input defined in
323 * common.h. Remaining arguments are values for the type argument. See
324 * field_type(3X) for validation types.
326 * FIXME form validation is buggy (integers).
328 WIN *
329 construct_input (const char *title, const char *init, int lines, int reset,
330 const char *extra_help, input_func * func, void *arg,
331 wint_t key, struct input_data_s * id, int history,
332 window_resize_func rfunc, int type, ...)
334 WIN *win;
335 struct input_s *in;
336 int l = (lines > 0) ? lines : 1;
337 va_list ap;
338 FIELDTYPE *ft = NULL;
339 int eh = 0;
341 l += 2;
342 in = Calloc (1, sizeof (struct input_s));
343 in->w = INPUT_WIDTH;
345 if (extra_help)
347 char *tmp = strdup (extra_help);
349 in->extra = split_str (tmp, "\n", &eh, &in->w, 0);
350 l += eh;
351 free (tmp);
354 if (title)
355 l++;
357 in->h = l + 1;
358 in->func = func;
359 in->arg = arg;
360 in->c = key;
361 in->lines = (lines) ? lines : 1;
362 win = window_create (title, in->h, in->w, CALCPOSY (in->h), CALCPOSX (in->w),
363 get_input, in, id->efunc,
364 rfunc ? rfunc : input_resize_func);
365 in = win->data;
366 in->hist = history;
367 in->data = id;
368 in->reset = reset;
369 in->fields[0] = new_field (in->lines, in->w - 2, 0, 0, 0, 0);
370 va_start (ap, type);
372 switch (type)
374 case FIELD_TYPE_PGN_ROUND:
375 ft = new_fieldtype (NULL, validate_pgn_round);
376 set_field_type (in->fields[0], ft);
377 break;
378 case FIELD_TYPE_PGN_RESULT:
379 ft = new_fieldtype (NULL, validate_pgn_result);
380 set_field_type (in->fields[0], ft);
381 break;
382 case FIELD_TYPE_PGN_DATE:
383 ft = new_fieldtype (NULL, validate_pgn_date);
384 set_field_type (in->fields[0], ft);
385 break;
386 case FIELD_TYPE_PGN_TAG_NAME:
387 ft = new_fieldtype (NULL, validate_pgn_tag_name);
388 set_field_type (in->fields[0], ft);
389 break;
390 case FIELD_TYPE_ALNUM:
391 set_field_type (in->fields[0], TYPE_ALNUM, va_arg (ap, int));
392 break;
393 case FIELD_TYPE_ALPHA:
394 set_field_type (in->fields[0], TYPE_ALPHA, va_arg (ap, int));
395 break;
396 case FIELD_TYPE_ENUM:
397 set_field_type (in->fields[0], TYPE_ENUM, va_arg (ap, char **),
398 va_arg (ap, int), va_arg (ap, int));
399 break;
400 case FIELD_TYPE_INTEGER:
401 set_field_type (in->fields[0], TYPE_INTEGER, va_arg (ap, int),
402 va_arg (ap, long), va_arg (ap, long));
403 break;
404 case FIELD_TYPE_NUMERIC:
405 set_field_type (in->fields[0], TYPE_NUMERIC, va_arg (ap, int),
406 va_arg (ap, double), va_arg (ap, double));
407 break;
408 case FIELD_TYPE_REGEXP:
409 set_field_type (in->fields[0], TYPE_REGEXP, va_arg (ap, char *));
410 break;
411 /* Solaris 5.9 */
412 #ifdef HAVE_TYPE_IPV4
413 case FIELD_TYPE_IPV4:
414 set_field_type (in->fields[0], TYPE_IPV4);
415 break;
416 #endif
417 default:
418 set_field_type (in->fields[0], NULL);
419 break;
422 va_end (ap);
424 if (init)
426 strncpy (in->buf, init, sizeof (in->buf) - 1);
427 in->buf[sizeof (in->buf) - 1] = 0;
428 set_field_buffer (in->fields[0], 0, init);
431 in->ft = ft;
433 if (in->lines == 1)
434 field_opts_off (in->fields[0], O_STATIC);
436 if (in->buf[0])
437 set_field_buffer (in->fields[0], 0, in->buf);
439 field_opts_off (in->fields[0], O_BLANK | O_AUTOSKIP);
440 in->fields[1] = NULL;
441 in->f = new_form (in->fields);
442 form_opts_off (in->f, O_BS_OVERLOAD);
443 set_form_win (in->f, win->w);
444 in->sw = derwin (win->w, in->lines, in->w - 2, 2, 1);
445 set_form_sub (in->f, in->sw);
446 post_form (in->f);
447 form_driver (in->f, REQ_END_FIELD);
448 keypad (win->w, TRUE);
449 curs_set (1);
450 wbkgd (win->w, CP_INPUT_WINDOW);
451 (*win->func) (win);
452 return win;