Update copyright year to 2015.
[cboard.git] / src / input.c
blob0da2e74e6ec0c7e1ea0e2db4ab90ff45ae28e072
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2002-2015 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 {
43 char *str;
44 struct input_history_s *next;
45 struct input_history_s *prev;
46 struct input_history_s *head;
47 } *input_history[INPUT_HIST_MAX];
49 static void add_input_history(int which, const char *str)
51 struct input_history_s *new;
52 struct input_history_s *p = NULL;
54 if (!str || !*str || which < 0)
55 return;
57 if (input_history[which])
58 for (p = input_history[which]->head; p->next; p = p->next);
60 new = calloc(1, sizeof(struct input_history_s));
61 if (!new) {
62 fprintf(stderr, "Out of core!\n");
63 exit (EXIT_FAILURE);
66 new->str = strdup(str);
67 new->prev = p;
69 if (p) {
70 p->next = new;
71 new->head = p->head;
73 else
74 new->head = new;
76 input_history[which] = p ? p->next : new;
79 static bool validate_pgn_tag_name(int c, const void *arg)
81 if (!isalnum(c) && c != '_')
82 return FALSE;
84 return TRUE;
87 static bool validate_pgn_date(int c, const void *arg)
89 if (!isdigit(c) && c != '.' && c != '?')
90 return FALSE;
92 return TRUE;
95 static bool validate_pgn_round(int c, const void *arg)
97 if (!isdigit(c) && c != '.' && c != '-' && c != '?')
98 return FALSE;
100 return TRUE;
103 static bool validate_pgn_result(int c, const void *arg)
105 if (c != '1' && c != '0' && c != '2' && c != '*' && c != '/' && c != '-')
106 return FALSE;
108 return TRUE;
111 static int get_input(WIN *win)
113 struct input_s *in = win->data;
114 char *tmp;
115 struct input_data_s *data = in->data;
116 int i, n;
117 char *prompt = _("Type F1 for help");
118 char str[MB_CUR_MAX];
120 curs_set(1);
121 window_draw_title(win->w, win->title, in->w, CP_INPUT_TITLE,
122 CP_INPUT_BORDER);
124 if (in->extra) {
125 for (i = 0; in->extra[i]; i++)
126 window_draw_prompt(win->w, in->lines + 2 + i, in->w, in->extra[i],
127 CP_INPUT_PROMPT);
129 window_draw_prompt(win->w, in->lines + 2 + i, in->w, prompt,
130 CP_INPUT_PROMPT);
132 else
133 window_draw_prompt(win->w, in->lines + 2, in->w, prompt,
134 CP_INPUT_PROMPT);
136 if (in->func && in->c && win->c == in->c) {
137 (*in->func)(in);
138 return 1;
141 switch (win->c) {
142 case CTRL_KEY('X'):
143 form_driver(in->f, REQ_DEL_WORD);
144 break;
145 case CTRL_KEY('B'):
146 form_driver(in->f, REQ_PREV_WORD);
147 break;
148 case CTRL_KEY('W'):
149 form_driver(in->f, REQ_NEXT_WORD);
150 break;
151 case KEY_HOME:
152 case CTRL_KEY('A'):
153 form_driver(in->f, REQ_BEG_LINE);
154 break;
155 case KEY_END:
156 case CTRL_KEY('E'):
157 form_driver(in->f, REQ_END_LINE);
158 break;
159 case CTRL_KEY('K'):
160 form_driver(in->f, REQ_CLR_EOL);
161 break;
162 case CTRL_KEY('U'):
163 form_driver(in->f, REQ_CLR_FIELD);
164 break;
165 case KEY_F(1):
166 message(_("Line Editing Keys"), ANY_KEY_STR,
167 "%s",
169 "UP/DOWN/LEFT/RIGHT - position cursor (multiline)\n"
170 " UP/CTRL-P - previous input history\n"
171 " DOWN/CTRL-N - next input history\n"
172 " HOME/CTRL-A - move cursor to the beginning of line\n"
173 " END/CTRL-E - move cursor to the end of line\n"
174 " CTRL-B/W - move cursor to previous/next word\n"
175 " CTRL-X - delete word under cursor\n"
176 " CTRL-K - delete from cursor to end of line\n"
177 " CTRL-U - clear entire input field\n"
178 " BACKSPACE - delete previous character\n"
179 " ESCAPE - quit without changes\n"
180 " ENTER - quit with changes"));
181 break;
182 case KEY_LEFT:
183 form_driver(in->f, REQ_LEFT_CHAR);
184 break;
185 case KEY_RIGHT:
186 form_driver(in->f, REQ_RIGHT_CHAR);
187 break;
188 case CTRL_KEY('P'):
189 case KEY_UP:
190 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1) {
191 set_field_buffer(in->fields[0], 0, input_history[in->hist]->str);
192 form_driver(in->f, REQ_END_LINE);
194 if (!input_history[in->hist]->prev)
195 input_history[in->hist] = input_history[in->hist]->head;
196 else
197 input_history[in->hist] = input_history[in->hist]->prev;
199 break;
202 form_driver(in->f, REQ_UP_CHAR);
203 break;
204 case CTRL_KEY('N'):
205 case KEY_DOWN:
206 if (in->hist >= 0 && input_history[in->hist] && in->lines == 1) {
207 if (!input_history[in->hist]->next) {
208 set_field_buffer(in->fields[0], 0, NULL);
209 form_driver(in->f, REQ_CLR_FIELD);
210 break;
213 input_history[in->hist] = input_history[in->hist]->next;
214 set_field_buffer(in->fields[0], 0, input_history[in->hist]->str);
215 form_driver(in->f, REQ_END_LINE);
216 break;
219 form_driver(in->f, REQ_DOWN_CHAR);
220 break;
221 case '\010':
222 case KEY_BACKSPACE:
223 form_driver(in->f, REQ_DEL_PREV);
224 break;
225 case '\n':
226 tmp = field_buffer(in->fields[0], 0);
227 goto done;
228 case KEY_ESCAPE:
229 if (in->reset)
230 tmp = NULL;
231 else
232 tmp = (in->buf[0]) ? in->buf : NULL;
233 goto done;
234 default:
235 n = wctomb (str, win->c);
236 for (i = 0; n != -1 && i < n; i++)
237 form_driver(in->f, (unsigned char )str[i]);
238 break;
241 form_driver(in->f, REQ_VALIDATION);
242 return 1;
244 done:
245 if (tmp) {
246 tmp = trim(tmp);
248 if (tmp[0]) {
249 strncpy(in->buf, tmp, sizeof(in->buf)-1);
250 in->buf[sizeof(in->buf)-1] = 0;
252 else
253 in->buf[0] = 0;
255 else
256 in->buf[0] = 0;
258 unpost_form(in->f);
259 free_form(in->f);
260 free_field(in->fields[0]);
262 if (in->extra) {
263 for (i = 0; in->extra[i]; i++)
264 free(in->extra[i]);
266 free(in->extra);
269 data->str = (in->buf[0]) ? strdup(in->buf) : NULL;
271 if (in->lines == 1)
272 add_input_history(in->hist, in->buf);
274 win->data = data;
275 free_fieldtype(in->ft);
276 free(in);
277 curs_set(0);
278 return 0;
282 * This function prompts for input. The init argument is the initial value.
283 * The lines argument is how many lines the field is. If zero, then it is
284 * dynamically determined based on the init argument or INPUT_WIDTH if init is
285 * NULL.
287 * The reset argument is whether pressing ESC returns the initial value or
288 * NULL.
290 * The extra_help argument is an extra line of help prompt normally used with
291 * the custom_func argument. The custom_func argument is a pointer to a
292 * function of type void which takes one pointer-to-void argument. This
293 * function is called when the ckey argument is pressed.
295 * The efunc parameter is the function that is ran when the window is done
296 * getting input or NULL. This function is ran just before the window is
297 * destroyed.
299 * The type argument is the type of validation for the input defined in
300 * common.h. Remaining arguments are values for the type argument. See
301 * field_type(3X) for validation types.
303 * FIXME form validation is buggy (integers).
305 WIN *construct_input(const char *title, const char *init, int lines, int reset,
306 const char *extra_help, input_func *func, void *arg, wint_t key,
307 struct input_data_s *id, int history, int type, ...)
309 WIN *win;
310 struct input_s *in;
311 int l = (lines > 0) ? lines : 1;
312 va_list ap;
313 FIELDTYPE *ft = NULL;
314 int eh = 0;
316 l += 2;
317 in = Calloc(1, sizeof(struct input_s));
318 in->w = INPUT_WIDTH;
320 if (extra_help) {
321 char *tmp = strdup(extra_help);
323 in->extra = split_str(tmp, "\n", &eh, &in->w, 0);
324 l += eh;
325 free(tmp);
328 if (title)
329 l++;
331 in->h = l + 1;
332 in->func = func;
333 in->arg = arg;
334 in->c = key;
335 in->lines = (lines) ? lines : 1;
336 win = window_create(title, in->h, in->w, CALCPOSY(in->h), CALCPOSX(in->w),
337 get_input, in, id->efunc);
338 in = win->data;
339 in->hist = history;
340 in->data = id;
341 in->reset = reset;
342 in->fields[0] = new_field(in->lines, in->w - 2, 0, 0, 0, 0);
343 va_start(ap, type);
345 switch (type) {
346 case FIELD_TYPE_PGN_ROUND:
347 ft = new_fieldtype(NULL, validate_pgn_round);
348 set_field_type(in->fields[0], ft);
349 break;
350 case FIELD_TYPE_PGN_RESULT:
351 ft = new_fieldtype(NULL, validate_pgn_result);
352 set_field_type(in->fields[0], ft);
353 break;
354 case FIELD_TYPE_PGN_DATE:
355 ft = new_fieldtype(NULL, validate_pgn_date);
356 set_field_type(in->fields[0], ft);
357 break;
358 case FIELD_TYPE_PGN_TAG_NAME:
359 ft = new_fieldtype(NULL, validate_pgn_tag_name);
360 set_field_type(in->fields[0], ft);
361 break;
362 case FIELD_TYPE_ALNUM:
363 set_field_type(in->fields[0], TYPE_ALNUM, va_arg(ap, int));
364 break;
365 case FIELD_TYPE_ALPHA:
366 set_field_type(in->fields[0], TYPE_ALPHA, va_arg(ap, int));
367 break;
368 case FIELD_TYPE_ENUM:
369 set_field_type(in->fields[0], TYPE_ENUM, va_arg(ap, char **),
370 va_arg(ap, int), va_arg(ap, int));
371 break;
372 case FIELD_TYPE_INTEGER:
373 set_field_type(in->fields[0], TYPE_INTEGER, va_arg(ap, int),
374 va_arg(ap, long), va_arg(ap, long));
375 break;
376 case FIELD_TYPE_NUMERIC:
377 set_field_type(in->fields[0], TYPE_NUMERIC, va_arg(ap, int),
378 va_arg(ap, double), va_arg(ap, double));
379 break;
380 case FIELD_TYPE_REGEXP:
381 set_field_type(in->fields[0], TYPE_REGEXP, va_arg(ap, char *));
382 break;
383 /* Solaris 5.9 */
384 #ifdef HAVE_TYPE_IPV4
385 case FIELD_TYPE_IPV4:
386 set_field_type(in->fields[0], TYPE_IPV4);
387 break;
388 #endif
389 default:
390 set_field_type(in->fields[0], NULL);
391 break;
394 va_end(ap);
396 if (init) {
397 strncpy(in->buf, init, sizeof(in->buf)-1);
398 in->buf[sizeof(in->buf)-1] = 0;
399 set_field_buffer(in->fields[0], 0, init);
402 in->ft = ft;
404 if (in->lines == 1)
405 field_opts_off(in->fields[0], O_STATIC);
407 if (in->buf[0])
408 set_field_buffer(in->fields[0], 0, in->buf);
410 field_opts_off(in->fields[0], O_BLANK|O_AUTOSKIP);
411 in->fields[1] = NULL;
412 in->f = new_form(in->fields);
413 form_opts_off(in->f, O_BS_OVERLOAD);
414 set_form_win(in->f, win->w);
415 in->sw = derwin(win->w, in->lines, in->w - 2, 2, 1);
416 set_form_sub(in->f, in->sw);
417 post_form(in->f);
418 form_driver(in->f, REQ_END_FIELD);
419 keypad(win->w, TRUE);
420 curs_set(1);
421 wbkgd(win->w, CP_INPUT_WINDOW);
422 (*win->func)(win);
423 return win;