Autotools portability fixes and cleanups.
[cboard.git] / src / input.c
blob7aebeba85c7fb605692c45ac9f83bcc5dbb82d8e
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2011 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 #ifdef HAVE_FORM_H
34 #include <form.h>
35 #endif
37 #ifdef HAVE_PANEL_H
38 #include <panel.h>
39 #endif
41 #include "chess.h"
42 #include "conf.h"
43 #include "colors.h"
44 #include "window.h"
45 #include "strings.h"
46 #include "misc.h"
47 #include "message.h"
48 #include "input.h"
50 #ifdef WITH_DMALLOC
51 #include <dmalloc.h>
52 #endif
54 const char *inputhelp = {
55 "UP/DOWN/LEFT/RIGHT - position cursor (multiline)\n" \
56 " UP/CTRL-P - previous input history\n" \
57 " DOWN/CTRL-N - next input history\n" \
58 " HOME/CTRL-A - move cursor to the beginning of line\n" \
59 " END/CTRL-E - move cursor to the end of line\n" \
60 " CTRL-B/W - move cursor to previous/next word\n" \
61 " CTRL-X - delete word under cursor\n" \
62 " CTRL-K - delete from cursor to end of line\n" \
63 " CTRL-U - clear entire input field\n" \
64 " BACKSPACE - delete previous character\n" \
65 " ESCAPE - quit without changes\n" \
66 " ENTER - quit with changes"
69 static struct input_history_s {
70 char *str;
71 struct input_history_s *next;
72 struct input_history_s *prev;
73 struct input_history_s *head;
74 } *input_history[INPUT_HIST_MAX];
76 static void add_input_history(int which, const char *str)
78 struct input_history_s *new;
79 struct input_history_s *p = NULL;
81 if (!str || !*str || which < 0)
82 return;
84 if (input_history[which])
85 for (p = input_history[which]->head; p->next; p = p->next);
87 new = calloc(1, sizeof(struct input_history_s));
88 new->str = strdup(str);
89 new->prev = p;
91 if (p) {
92 p->next = new;
93 new->head = p->head;
95 else
96 new->head = new;
98 input_history[which] = p ? p->next : new;
101 static bool validate_pgn_tag_name(int c, const void *arg)
103 if (!isalnum(c) && c != '_')
104 return FALSE;
106 return TRUE;
109 static bool validate_pgn_date(int c, const void *arg)
111 if (!isdigit(c) && c != '.' && c != '?')
112 return FALSE;
114 return TRUE;
117 static bool validate_pgn_round(int c, const void *arg)
119 if (!isdigit(c) && c != '.' && c != '-' && c != '?')
120 return FALSE;
122 return TRUE;
125 static bool validate_pgn_result(int c, const void *arg)
127 if (c != '1' && c != '0' && c != '2' && c != '*' && c != '/' && c != '-')
128 return FALSE;
130 return TRUE;
133 static int get_input(WIN *win)
135 struct input_s *in = win->data;
136 char *tmp;
137 struct input_data_s *data = in->data;
138 int i;
140 curs_set(1);
141 window_draw_title(win->w, win->title, in->w, CP_INPUT_TITLE,
142 CP_INPUT_BORDER);
144 if (in->extra) {
145 for (i = 0; in->extra[i]; i++)
146 window_draw_prompt(win->w, in->lines + 2 + i, in->w, in->extra[i],
147 CP_INPUT_PROMPT);
149 window_draw_prompt(win->w, in->lines + 2 + i, in->w, INPUT_HELP_PROMPT,
150 CP_INPUT_PROMPT);
152 else
153 window_draw_prompt(win->w, in->lines + 2, in->w, INPUT_HELP_PROMPT,
154 CP_INPUT_PROMPT);
156 if (in->func && in->c && win->c == in->c) {
157 (*in->func)(in);
158 return 1;
161 switch (win->c) {
162 case CTRL('X'):
163 form_driver(in->f, REQ_DEL_WORD);
164 break;
165 case CTRL('B'):
166 form_driver(in->f, REQ_PREV_WORD);
167 break;
168 case CTRL('W'):
169 form_driver(in->f, REQ_NEXT_WORD);
170 break;
171 case KEY_HOME:
172 case CTRL('A'):
173 form_driver(in->f, REQ_BEG_LINE);
174 break;
175 case KEY_END:
176 case CTRL('E'):
177 form_driver(in->f, REQ_END_LINE);
178 break;
179 case CTRL('K'):
180 form_driver(in->f, REQ_CLR_EOL);
181 break;
182 case CTRL('U'):
183 form_driver(in->f, REQ_CLR_FIELD);
184 break;
185 case KEY_F(1):
186 message(INPUT_HELP_TITLE, ANYKEY, "%s", inputhelp);
187 break;
188 case KEY_LEFT:
189 form_driver(in->f, REQ_LEFT_CHAR);
190 break;
191 case KEY_RIGHT:
192 form_driver(in->f, REQ_RIGHT_CHAR);
193 break;
194 case CTRL('P'):
195 case KEY_UP:
196 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1) {
197 set_field_buffer(in->fields[0], 0, input_history[in->hist]->str);
198 form_driver(in->f, REQ_END_LINE);
200 if (!input_history[in->hist]->prev)
201 input_history[in->hist] = input_history[in->hist]->head;
202 else
203 input_history[in->hist] = input_history[in->hist]->prev;
205 break;
208 form_driver(in->f, REQ_UP_CHAR);
209 break;
210 case CTRL('N'):
211 case KEY_DOWN:
212 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1) {
213 if (!input_history[in->hist]->next) {
214 set_field_buffer(in->fields[0], 0, NULL);
215 form_driver(in->f, REQ_CLR_FIELD);
216 break;
219 input_history[in->hist] = input_history[in->hist]->next;
220 set_field_buffer(in->fields[0], 0, input_history[in->hist]->str);
221 form_driver(in->f, REQ_END_LINE);
222 break;
225 form_driver(in->f, REQ_DOWN_CHAR);
226 break;
227 case '\010':
228 case KEY_BACKSPACE:
229 form_driver(in->f, REQ_DEL_PREV);
230 break;
231 case '\n':
232 tmp = field_buffer(in->fields[0], 0);
233 goto done;
234 case KEY_ESCAPE:
235 if (in->reset)
236 tmp = NULL;
237 else
238 tmp = (in->buf[0]) ? in->buf : NULL;
239 goto done;
240 default:
241 form_driver(in->f, (win->c & A_CHARTEXT));
242 break;
245 form_driver(in->f, REQ_VALIDATION);
246 return 1;
248 done:
249 if (tmp) {
250 tmp = trim(tmp);
252 if (tmp[0])
253 strncpy(in->buf, tmp, sizeof(in->buf));
254 else
255 in->buf[0] = 0;
257 else
258 in->buf[0] = 0;
260 unpost_form(in->f);
261 free_form(in->f);
262 free_field(in->fields[0]);
264 if (in->extra) {
265 for (i = 0; in->extra[i]; i++)
266 free(in->extra[i]);
268 free(in->extra);
271 data->str = (in->buf[0]) ? strdup(in->buf) : NULL;
273 if (in->lines == 1)
274 add_input_history(in->hist, in->buf);
276 win->data = data;
277 free_fieldtype(in->ft);
278 free(in);
279 curs_set(0);
280 return 0;
284 * This function prompts for input. The init argument is the initial value.
285 * The lines argument is how many lines the field is. If zero, then it is
286 * dynamically determined based on the init argument or INPUT_WIDTH if init is
287 * NULL.
289 * The reset argument is whether pressing ESC returns the initial value or
290 * NULL.
292 * The extra_help argument is an extra line of help prompt normally used with
293 * the custom_func argument. The custom_func argument is a pointer to a
294 * function of type void which takes one pointer-to-void argument. This
295 * function is called when the ckey argument is pressed.
297 * The efunc parameter is the function that is ran when the window is done
298 * getting input or NULL. This function is ran just before the window is
299 * destroyed.
301 * The type argument is the type of validation for the input defined in
302 * common.h. Remaining arguments are values for the type argument. See
303 * field_type(3X) for validation types.
305 * FIXME form validation is buggy (integers).
307 WIN *construct_input(const char *title, const char *init, int lines, int reset,
308 const char *extra_help, input_func *func, void *arg, int key,
309 struct input_data_s *id, int history, int type, ...)
311 WIN *win;
312 struct input_s *in;
313 int l = (lines > 0) ? lines : 1;
314 va_list ap;
315 FIELDTYPE *ft = NULL;
316 int eh = 0;
318 l += 2;
319 in = Calloc(1, sizeof(struct input_s));
320 in->w = INPUT_WIDTH;
322 if (extra_help) {
323 char *tmp = strdup(extra_help);
325 in->extra = split_str(tmp, "\n", &eh, &in->w, 0);
326 l += eh;
327 free(tmp);
330 if (title)
331 l++;
333 in->h = l + 1;
334 in->func = func;
335 in->arg = arg;
336 in->c = key;
337 in->lines = (lines) ? lines : 1;
338 win = window_create(title, in->h, in->w, CALCPOSY(in->h), CALCPOSX(in->w),
339 get_input, in, id->efunc);
340 in = win->data;
341 in->hist = history;
342 in->data = id;
343 in->reset = reset;
344 in->fields[0] = new_field(in->lines, in->w - 2, 0, 0, 0, 0);
345 va_start(ap, type);
347 switch (type) {
348 case FIELD_TYPE_PGN_ROUND:
349 ft = new_fieldtype(NULL, validate_pgn_round);
350 set_field_type(in->fields[0], ft);
351 break;
352 case FIELD_TYPE_PGN_RESULT:
353 ft = new_fieldtype(NULL, validate_pgn_result);
354 set_field_type(in->fields[0], ft);
355 break;
356 case FIELD_TYPE_PGN_DATE:
357 ft = new_fieldtype(NULL, validate_pgn_date);
358 set_field_type(in->fields[0], ft);
359 break;
360 case FIELD_TYPE_PGN_TAG_NAME:
361 ft = new_fieldtype(NULL, validate_pgn_tag_name);
362 set_field_type(in->fields[0], ft);
363 break;
364 case FIELD_TYPE_ALNUM:
365 set_field_type(in->fields[0], TYPE_ALNUM, va_arg(ap, int));
366 break;
367 case FIELD_TYPE_ALPHA:
368 set_field_type(in->fields[0], TYPE_ALPHA, va_arg(ap, int));
369 break;
370 case FIELD_TYPE_ENUM:
371 set_field_type(in->fields[0], TYPE_ENUM, va_arg(ap, char **),
372 va_arg(ap, int), va_arg(ap, int));
373 break;
374 case FIELD_TYPE_INTEGER:
375 set_field_type(in->fields[0], TYPE_INTEGER, va_arg(ap, int),
376 va_arg(ap, long), va_arg(ap, long));
377 break;
378 case FIELD_TYPE_NUMERIC:
379 set_field_type(in->fields[0], TYPE_NUMERIC, va_arg(ap, int),
380 va_arg(ap, double), va_arg(ap, double));
381 break;
382 case FIELD_TYPE_REGEXP:
383 set_field_type(in->fields[0], TYPE_REGEXP, va_arg(ap, char *));
384 break;
385 /* Solaris 5.9 */
386 #ifdef HAVE_TYPE_IPV4
387 case FIELD_TYPE_IPV4:
388 set_field_type(in->fields[0], TYPE_IPV4);
389 break;
390 #endif
391 default:
392 set_field_type(in->fields[0], NULL);
393 break;
396 va_end(ap);
398 if (init) {
399 strncpy(in->buf, init, sizeof(in->buf));
400 set_field_buffer(in->fields[0], 0, init);
403 in->ft = ft;
405 if (in->lines == 1)
406 field_opts_off(in->fields[0], O_STATIC);
408 if (in->buf)
409 set_field_buffer(in->fields[0], 0, in->buf);
411 field_opts_off(in->fields[0], O_BLANK|O_AUTOSKIP);
412 in->fields[1] = NULL;
413 in->f = new_form(in->fields);
414 form_opts_off(in->f, O_BS_OVERLOAD);
415 set_form_win(in->f, win->w);
416 in->sw = derwin(win->w, in->lines, in->w - 2, 2, 1);
417 set_form_sub(in->f, in->sw);
418 post_form(in->f);
419 form_driver(in->f, REQ_END_FIELD);
420 keypad(win->w, TRUE);
421 curs_set(1);
422 wbkgd(win->w, CP_INPUT_WINDOW);
423 (*win->func)(win);
424 return win;