]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/edit_field.c
ncurses 6.0 - patch 20170318
[ncurses.git] / test / edit_field.c
1 /****************************************************************************
2  * Copyright (c) 2003-2013,2014 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * $Id: edit_field.c,v 1.24 2014/09/05 08:39:52 tom Exp $
30  *
31  * A wrapper for form_driver() which keeps track of the user's editing changes
32  * for each field, and makes the resulting length available as a
33  * null-terminated string in field_buffer(field,1).
34  *
35  * Thomas Dickey - 2003/4/26.
36  */
37
38 #include <test.priv.h>
39
40 #if USE_LIBFORM
41
42 #include <edit_field.h>
43
44 static struct {
45     int code;
46     int result;
47     const char *help;
48 } commands[] = {
49
50     {
51         CTRL('A'), REQ_NEXT_CHOICE, ""
52     },
53     {
54         CTRL('B'), REQ_PREV_WORD, "go to previous word"
55     },
56     {
57         CTRL('C'), REQ_CLR_EOL, "clear to end of line"
58     },
59     {
60         CTRL('D'), REQ_DOWN_FIELD, "move downward to field"
61     },
62     {
63         CTRL('E'), REQ_END_FIELD, "go to end of field"
64     },
65     {
66         CTRL('F'), REQ_NEXT_PAGE, "go to next page"
67     },
68     {
69         CTRL('G'), REQ_DEL_WORD, "delete current word"
70     },
71     {
72         CTRL('H'), REQ_DEL_PREV, "delete previous character"
73     },
74     {
75         CTRL('I'), REQ_INS_CHAR, "insert character"
76     },
77     {
78         CTRL('K'), REQ_CLR_EOF, "clear to end of field"
79     },
80     {
81         CTRL('L'), REQ_LEFT_FIELD, "go to field to left"
82     },
83     {
84         CTRL('M'), REQ_NEW_LINE, "insert/overlay new line"
85     },
86     {
87         CTRL('N'), REQ_NEXT_FIELD, "go to next field"
88     },
89     {
90         CTRL('O'), REQ_INS_LINE, "insert blank line at cursor"
91     },
92     {
93         CTRL('P'), REQ_PREV_FIELD, "go to previous field"
94     },
95     {
96         CTRL('Q'), MY_QUIT, "exit form"
97     },
98     {
99         CTRL('R'), REQ_RIGHT_FIELD, "go to field to right"
100     },
101     {
102         CTRL('S'), REQ_BEG_FIELD, "go to beginning of field"
103     },
104     {
105         CTRL('T'), MY_EDT_MODE, "toggle O_EDIT mode, clear field status",
106     },
107     {
108         CTRL('U'), REQ_UP_FIELD, "move upward to field"
109     },
110     {
111         CTRL('V'), REQ_DEL_CHAR, "delete character"
112     },
113     {
114         CTRL('W'), REQ_NEXT_WORD, "go to next word"
115     },
116     {
117         CTRL('X'), REQ_CLR_FIELD, "clear field"
118     },
119     {
120         CTRL('Y'), REQ_DEL_LINE, "delete line"
121     },
122     {
123         CTRL('Z'), REQ_PREV_CHOICE, ""
124     },
125     {
126         CTRL('['), MY_QUIT, "exit form"
127     },
128     {
129         CTRL(']'), MY_INS_MODE, "toggle REQ_INS_MODE/REQ_OVL_MODE",
130     },
131     {
132         KEY_F(1), MY_HELP, "show this screen",
133     },
134     {
135         KEY_BACKSPACE, REQ_DEL_PREV, "delete previous character"
136     },
137     {
138         KEY_DOWN, REQ_DOWN_CHAR, "move down 1 character"
139     },
140     {
141         KEY_END, REQ_LAST_FIELD, "go to last field"
142     },
143     {
144         KEY_HOME, REQ_FIRST_FIELD, "go to first field"
145     },
146     {
147         KEY_LEFT, REQ_LEFT_CHAR, "move left 1 character"
148     },
149     {
150         KEY_LL, REQ_LAST_FIELD, "go to last field"
151     },
152     {
153         KEY_NEXT, REQ_NEXT_FIELD, "go to next field"
154     },
155     {
156         KEY_NPAGE, REQ_NEXT_PAGE, "go to next page"
157     },
158     {
159         KEY_PPAGE, REQ_PREV_PAGE, "go to previous page"
160     },
161     {
162         KEY_PREVIOUS, REQ_PREV_FIELD, "go to previous field"
163     },
164     {
165         KEY_RIGHT, REQ_RIGHT_CHAR, "move right 1 character"
166     },
167     {
168         KEY_UP, REQ_UP_CHAR, "move up 1 character"
169     }
170 };
171
172 static WINDOW *old_window;
173
174 static void
175 begin_popup(void)
176 {
177     doupdate();
178     old_window = dupwin(curscr);
179 }
180
181 static void
182 end_popup(void)
183 {
184     touchwin(old_window);
185     wnoutrefresh(old_window);
186     doupdate();
187     delwin(old_window);
188 }
189
190 /*
191  * Display a temporary window listing the keystroke-commands we recognize.
192  */
193 void
194 help_edit_field(void)
195 {
196     int x0 = 4;
197     int y0 = 2;
198     int y1 = 0;
199     int y2 = 0;
200     int wide = COLS - ((x0 + 1) * 2);
201     int high = LINES - ((y0 + 1) * 2);
202     WINDOW *help = newwin(high, wide, y0, x0);
203     WINDOW *data = newpad(2 + SIZEOF(commands), wide - 4);
204     unsigned n;
205     int ch = ERR;
206
207     begin_popup();
208
209     keypad(help, TRUE);
210     keypad(data, TRUE);
211     waddstr(data, "Defined form edit/traversal keys:\n");
212     for (n = 0; n < SIZEOF(commands); ++n) {
213         const char *name;
214 #ifdef NCURSES_VERSION
215         if ((name = form_request_name(commands[n].result)) == 0)
216 #endif
217             name = commands[n].help;
218         wprintw(data, "%s -- %s\n",
219                 keyname(commands[n].code),
220                 name != 0 ? name : commands[n].help);
221     }
222     waddstr(data, "Arrow keys move within a field as you would expect.");
223     y2 = getcury(data);
224
225     do {
226         switch (ch) {
227         case KEY_HOME:
228             y1 = 0;
229             break;
230         case KEY_END:
231             y1 = y2;
232             break;
233         case KEY_PREVIOUS:
234         case KEY_PPAGE:
235             if (y1 > 0) {
236                 y1 -= high / 2;
237                 if (y1 < 0)
238                     y1 = 0;
239             } else {
240                 beep();
241             }
242             break;
243         case KEY_NEXT:
244         case KEY_NPAGE:
245             if (y1 < y2) {
246                 y1 += high / 2;
247                 if (y1 >= y2)
248                     y1 = y2;
249             } else {
250                 beep();
251             }
252             break;
253         case CTRL('P'):
254         case KEY_UP:
255             if (y1 > 0)
256                 --y1;
257             else
258                 beep();
259             break;
260         case CTRL('N'):
261         case KEY_DOWN:
262             if (y1 < y2)
263                 ++y1;
264             else
265                 beep();
266             break;
267         default:
268             beep();
269             break;
270         case ERR:
271             break;
272         }
273         werase(help);
274         box(help, 0, 0);
275         wnoutrefresh(help);
276         pnoutrefresh(data, y1, 0, y0 + 1, x0 + 1, high, wide);
277         doupdate();
278     } while ((ch = wgetch(data)) != ERR && ch != QUIT && ch != ESCAPE);
279     werase(help);
280     wrefresh(help);
281     delwin(help);
282     delwin(data);
283
284     end_popup();
285 }
286
287 static int
288 offset_in_field(FORM * form)
289 {
290     FIELD *field = current_field(form);
291     int currow, curcol;
292
293     form_getyx(form, currow, curcol);
294     return curcol + currow * field->dcols;
295 }
296
297 static void
298 inactive_field(FIELD * f)
299 {
300     set_field_back(f, field_attrs(f)->background);
301 }
302
303 FieldAttrs *
304 field_attrs(FIELD * f)
305 {
306     return (FieldAttrs *) field_userptr(f);
307 }
308
309 static int
310 buffer_length(FIELD * f)
311 {
312     return field_attrs(f)->row_lengths[0];
313 }
314
315 static void
316 set_buffer_length(FIELD * f, int length)
317 {
318     field_attrs(f)->row_lengths[0] = length;
319 }
320
321 /*
322  * The userptr is used in edit_field.c's inactive_field(), as well as for
323  * keeping track of the actual lengths of lines in a multiline field.
324  */
325 void
326 init_edit_field(FIELD * f, char *value)
327 {
328     char empty[1];
329     FieldAttrs *ptr = field_attrs(f);
330     if (ptr == 0) {
331         int rows, cols, frow, fcol, nrow, nbuf;
332
333         ptr = typeCalloc(FieldAttrs, (size_t) 1);
334         ptr->background = field_back(f);
335         if (field_info(f, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK) {
336             ptr->row_count = nrow;
337             ptr->row_lengths = typeCalloc(int, (size_t) nrow + 1);
338         }
339     }
340     if (value == 0) {
341         value = empty;
342         *value = '\0';
343     }
344     set_field_userptr(f, (void *) ptr);
345     set_field_buffer(f, 0, value);      /* will be formatted */
346     set_field_buffer(f, 1, value);      /* will be unformatted */
347     set_buffer_length(f, (int) strlen(value));
348 }
349
350 int
351 edit_field(FORM * form, int *result)
352 {
353     int ch = wgetch(form_win(form));
354     int status;
355     FIELD *before;
356     unsigned n;
357     int length;
358     int before_row;
359     int before_col;
360     int before_off = offset_in_field(form);
361
362     form_getyx(form, before_row, before_col);
363     before = current_field(form);
364     set_field_back(before, A_NORMAL);
365     if (ch <= KEY_MAX) {
366         set_field_back(before, A_REVERSE);
367     } else if (ch <= MAX_FORM_COMMAND) {
368         inactive_field(before);
369     }
370
371     *result = ch;
372     for (n = 0; n < SIZEOF(commands); ++n) {
373         if (commands[n].code == ch) {
374             *result = commands[n].result;
375             break;
376         }
377     }
378
379     status = form_driver(form, *result);
380
381     if (status == E_OK) {
382         bool modified = TRUE;
383
384         length = buffer_length(before);
385         if (length < before_off)
386             length = before_off;
387         switch (*result) {
388         case REQ_CLR_EOF:
389             length = before_off;
390             break;
391         case REQ_CLR_EOL:
392             if ((int) (before_row + 1) == (int) (before->rows))
393                 length = before_off;
394             break;
395         case REQ_CLR_FIELD:
396             length = 0;
397             break;
398         case REQ_DEL_CHAR:
399             if (length > before_off)
400                 --length;
401             break;
402         case REQ_DEL_PREV:
403             if (length > 0) {
404                 if (before_col > 0) {
405                     --length;
406                 } else if (before_row > 0) {
407                     length -= before->cols + before_col;
408                 }
409             }
410             break;
411         case REQ_NEW_LINE:
412             length += before->cols;
413             break;
414 #if 0
415             /* FIXME: finish these */
416         case REQ_DEL_LINE:      /* delete line */
417         case REQ_DEL_WORD:      /* delete word at cursor */
418         case REQ_INS_CHAR:      /* insert blank char at cursor */
419         case REQ_INS_LINE:      /* insert blank line at cursor */
420         case REQ_INS_MODE:      /* begin insert mode */
421         case REQ_OVL_MODE:      /* begin overlay mode */
422 #endif
423             /* ignore all of the motion commands */
424         case REQ_SCR_BCHAR:     /* FALLTHRU */
425         case REQ_SCR_BHPAGE:    /* FALLTHRU */
426         case REQ_SCR_BLINE:     /* FALLTHRU */
427         case REQ_SCR_BPAGE:     /* FALLTHRU */
428         case REQ_SCR_FCHAR:     /* FALLTHRU */
429         case REQ_SCR_FHPAGE:    /* FALLTHRU */
430         case REQ_SCR_FLINE:     /* FALLTHRU */
431         case REQ_SCR_FPAGE:     /* FALLTHRU */
432         case REQ_SCR_HBHALF:    /* FALLTHRU */
433         case REQ_SCR_HBLINE:    /* FALLTHRU */
434         case REQ_SCR_HFHALF:    /* FALLTHRU */
435         case REQ_SCR_HFLINE:    /* FALLTHRU */
436         case REQ_BEG_FIELD:     /* FALLTHRU */
437         case REQ_BEG_LINE:      /* FALLTHRU */
438         case REQ_DOWN_CHAR:     /* FALLTHRU */
439         case REQ_DOWN_FIELD:    /* FALLTHRU */
440         case REQ_END_FIELD:     /* FALLTHRU */
441         case REQ_END_LINE:      /* FALLTHRU */
442         case REQ_FIRST_FIELD:   /* FALLTHRU */
443         case REQ_FIRST_PAGE:    /* FALLTHRU */
444         case REQ_LAST_FIELD:    /* FALLTHRU */
445         case REQ_LAST_PAGE:     /* FALLTHRU */
446         case REQ_LEFT_CHAR:     /* FALLTHRU */
447         case REQ_LEFT_FIELD:    /* FALLTHRU */
448         case REQ_NEXT_CHAR:     /* FALLTHRU */
449         case REQ_NEXT_CHOICE:   /* FALLTHRU */
450         case REQ_NEXT_FIELD:    /* FALLTHRU */
451         case REQ_NEXT_LINE:     /* FALLTHRU */
452         case REQ_NEXT_PAGE:     /* FALLTHRU */
453         case REQ_NEXT_WORD:     /* FALLTHRU */
454         case REQ_PREV_CHAR:     /* FALLTHRU */
455         case REQ_PREV_CHOICE:   /* FALLTHRU */
456         case REQ_PREV_FIELD:    /* FALLTHRU */
457         case REQ_PREV_LINE:     /* FALLTHRU */
458         case REQ_PREV_PAGE:     /* FALLTHRU */
459         case REQ_PREV_WORD:     /* FALLTHRU */
460         case REQ_RIGHT_CHAR:    /* FALLTHRU */
461         case REQ_RIGHT_FIELD:   /* FALLTHRU */
462         case REQ_SFIRST_FIELD:  /* FALLTHRU */
463         case REQ_SLAST_FIELD:   /* FALLTHRU */
464         case REQ_SNEXT_FIELD:   /* FALLTHRU */
465         case REQ_SPREV_FIELD:   /* FALLTHRU */
466         case REQ_UP_CHAR:       /* FALLTHRU */
467         case REQ_UP_FIELD:      /* FALLTHRU */
468         case REQ_VALIDATION:    /* FALLTHRU */
469             modified = FALSE;
470             break;
471
472         default:
473             modified = FALSE;
474             if (ch >= MIN_FORM_COMMAND) {
475                 beep();
476             } else if (isprint(ch)) {
477                 modified = TRUE;
478             }
479             break;
480         }
481
482         /*
483          * If we do not force a re-validation, then field_buffer 0 will
484          * be lagging by one character.
485          */
486         if (modified && form_driver(form, REQ_VALIDATION) == E_OK && *result
487             < MIN_FORM_COMMAND)
488             ++length;
489
490         set_buffer_length(before, length);
491     }
492
493     if (current_field(form) != before)
494         inactive_field(before);
495     return status;
496 }
497 #else
498
499 extern void no_edit_field(void);
500
501 void
502 no_edit_field(void)
503 {
504 }
505
506 #endif