ncurses 5.4
[ncurses.git] / test / edit_field.c
1 /*
2  * $Id: edit_field.c,v 1.7 2003/05/17 23:16:13 tom Exp $
3  *
4  * A wrapper for form_driver() which keeps track of the user's editing changes
5  * for each field, and makes the result available as a null-terminated string
6  * in field_buffer(field,1).
7  *
8  * Thomas Dickey - 2003/4/26.
9  */
10
11 #include <test.priv.h>
12
13 #if USE_LIBFORM
14
15 #include <edit_field.h>
16
17 #define MY_QUIT         EDIT_FIELD('q')
18 #define MY_INS_MODE     EDIT_FIELD('t')
19
20 static struct {
21     int code;
22     int result;
23     const char *help;
24 } commands[] = {
25
26     {
27         CTRL('A'), REQ_NEXT_CHOICE, ""
28     },
29     {
30         CTRL('B'), REQ_PREV_WORD, "go to previous word"
31     },
32     {
33         CTRL('C'), REQ_CLR_EOL, "clear to end of line"
34     },
35     {
36         CTRL('D'), REQ_DOWN_FIELD, "move downward to field"
37     },
38     {
39         CTRL('E'), REQ_END_FIELD, "go to end of field"
40     },
41     {
42         CTRL('F'), REQ_NEXT_PAGE, "go to next page"
43     },
44     {
45         CTRL('G'), REQ_DEL_WORD, "delete current word"
46     },
47     {
48         CTRL('H'), REQ_DEL_PREV, "delete previous character"
49     },
50     {
51         CTRL('I'), REQ_INS_CHAR, "insert character"
52     },
53     {
54         CTRL('K'), REQ_CLR_EOF, "clear to end of field"
55     },
56     {
57         CTRL('L'), REQ_LEFT_FIELD, "go to field to left"
58     },
59     {
60         CTRL('M'), REQ_NEW_LINE, "insert/overlay new line"
61     },
62     {
63         CTRL('N'), REQ_NEXT_FIELD, "go to next field"
64     },
65     {
66         CTRL('O'), REQ_INS_LINE, "insert blank line at cursor"
67     },
68     {
69         CTRL('P'), REQ_PREV_FIELD, "go to previous field"
70     },
71     {
72         CTRL('Q'), MY_QUIT, "exit form"
73     },
74     {
75         CTRL('R'), REQ_RIGHT_FIELD, "go to field to right"
76     },
77     {
78         CTRL('S'), REQ_BEG_FIELD, "go to beginning of field"
79     },
80     {
81         CTRL('U'), REQ_UP_FIELD, "move upward to field"
82     },
83     {
84         CTRL('V'), REQ_DEL_CHAR, "delete character"
85     },
86     {
87         CTRL('W'), REQ_NEXT_WORD, "go to next word"
88     },
89     {
90         CTRL('X'), REQ_CLR_FIELD, "clear field"
91     },
92     {
93         CTRL('Y'), REQ_DEL_LINE, "delete line"
94     },
95     {
96         CTRL('Z'), REQ_PREV_CHOICE, ""
97     },
98     {
99         CTRL('['), MY_QUIT, "exit form"
100     },
101     {
102         CTRL(']'), MY_INS_MODE, "toggle REQ_INS_MODE/REQ_OVL_MODE",
103     },
104     {
105         KEY_F(1), EDIT_FIELD('h'), "show this screen",
106     },
107     {
108         KEY_BACKSPACE, REQ_DEL_PREV, "delete previous character"
109     },
110     {
111         KEY_DOWN, REQ_DOWN_CHAR, "move down 1 character"
112     },
113     {
114         KEY_END, REQ_LAST_FIELD, "go to last field"
115     },
116     {
117         KEY_HOME, REQ_FIRST_FIELD, "go to first field"
118     },
119     {
120         KEY_LEFT, REQ_LEFT_CHAR, "move left 1 character"
121     },
122     {
123         KEY_LL, REQ_LAST_FIELD, "go to last field"
124     },
125     {
126         KEY_NEXT, REQ_NEXT_FIELD, "go to next field"
127     },
128     {
129         KEY_NPAGE, REQ_NEXT_PAGE, "go to next page"
130     },
131     {
132         KEY_PPAGE, REQ_PREV_PAGE, "go to previous page"
133     },
134     {
135         KEY_PREVIOUS, REQ_PREV_FIELD, "go to previous field"
136     },
137     {
138         KEY_RIGHT, REQ_RIGHT_CHAR, "move right 1 character"
139     },
140     {
141         KEY_UP, REQ_UP_CHAR, "move up 1 character"
142     }
143 };
144
145 static WINDOW *old_window;
146
147 static void
148 begin_popup(void)
149 {
150     doupdate();
151     old_window = dupwin(curscr);
152 }
153
154 static void
155 end_popup(void)
156 {
157     touchwin(old_window);
158     wnoutrefresh(old_window);
159     doupdate();
160     delwin(old_window);
161 }
162
163 /*
164  * Display a temporary window listing the keystroke-commands we recognize.
165  */
166 void
167 help_edit_field(void)
168 {
169     int x0 = 4;
170     int y0 = 2;
171     int y1 = 0;
172     int y2 = 0;
173     int wide = COLS - ((x0 + 1) * 2);
174     int high = LINES - ((y0 + 1) * 2);
175     WINDOW *help = newwin(high, wide, y0, x0);
176     WINDOW *data = newpad(2 + SIZEOF(commands), wide - 4);
177     unsigned n;
178     int ch = ERR;
179
180     begin_popup();
181
182     keypad(help, TRUE);
183     keypad(data, TRUE);
184     waddstr(data, "Defined form-traversal keys:\n");
185     for (n = 0; n < SIZEOF(commands); ++n) {
186         const char *name;
187 #ifdef NCURSES_VERSION
188         if ((name = form_request_name(commands[n].result)) == 0)
189 #endif
190             name = commands[n].help;
191         wprintw(data, "%s -- %s\n",
192                 keyname(commands[n].code),
193                 name != 0 ? name : commands[n].help);
194     }
195     waddstr(data, "Arrow keys move within a field as you would expect.");
196     y2 = getcury(data);
197
198     do {
199         switch (ch) {
200         case CTRL('P'):
201         case KEY_UP:
202             if (y1 > 0)
203                 --y1;
204             else
205                 beep();
206             break;
207         case CTRL('N'):
208         case KEY_DOWN:
209             if (y1 < y2)
210                 ++y1;
211             else
212                 beep();
213             break;
214         default:
215             beep();
216             break;
217         case ERR:
218             break;
219         }
220         werase(help);
221         box(help, 0, 0);
222         wnoutrefresh(help);
223         pnoutrefresh(data, y1, 0, y0 + 1, x0 + 1, high, wide);
224         doupdate();
225     } while ((ch = wgetch(data)) != ERR && ch != QUIT && ch != ESCAPE);
226     werase(help);
227     wrefresh(help);
228     delwin(help);
229     delwin(data);
230
231     end_popup();
232 }
233
234 static int
235 offset_in_field(FORM * form)
236 {
237     FIELD *field = current_field(form);
238     return form->curcol + form->currow * field->dcols;
239 }
240
241 int
242 edit_field(FORM * form, int *result)
243 {
244     int ch = wgetch(form_win(form));
245     int status;
246     FIELD *before;
247     FIELD *after;
248     unsigned n;
249     char lengths[80];
250     int length;
251     char *buffer;
252     int before_row = form->currow;
253     int before_col = form->curcol;
254     int before_off = offset_in_field(form);
255
256     before = current_field(form);
257     set_field_back(before, A_NORMAL);
258     if (ch <= KEY_MAX) {
259         set_field_back(before, A_REVERSE);
260     } else if (ch <= MAX_FORM_COMMAND) {
261         set_field_back(before, A_UNDERLINE);
262     }
263
264     *result = ch;
265     for (n = 0; n < SIZEOF(commands); ++n) {
266         if (commands[n].code == ch) {
267             *result = commands[n].result;
268             break;
269         }
270     }
271
272     status = form_driver(form, *result);
273
274     if (status == E_OK) {
275         bool modified = TRUE;
276
277         length = 0;
278         if ((buffer = field_buffer(before, 1)) != 0)
279             length = atoi(buffer);
280         if (length < before_off)
281             length = before_off;
282         switch (*result) {
283         case REQ_CLR_EOF:
284             length = before_off;
285             break;
286         case REQ_CLR_EOL:
287             if (before_row + 1 == before->rows)
288                 length = before_off;
289             break;
290         case REQ_CLR_FIELD:
291             length = 0;
292             break;
293         case REQ_DEL_CHAR:
294             if (length > before_off)
295                 --length;
296             break;
297         case REQ_DEL_PREV:
298             if (length > 0) {
299                 if (before_col > 0) {
300                     --length;
301                 } else if (before_row > 0) {
302                     length -= before->cols + before_col;
303                 }
304             }
305             break;
306         case REQ_NEW_LINE:
307             length += before->cols;
308             break;
309 #if 0
310             /* FIXME: finish these */
311         case REQ_DEL_LINE:      /* delete line */
312         case REQ_DEL_WORD:      /* delete word at cursor */
313         case REQ_INS_CHAR:      /* insert blank char at cursor */
314         case REQ_INS_LINE:      /* insert blank line at cursor */
315         case REQ_INS_MODE:      /* begin insert mode */
316         case REQ_OVL_MODE:      /* begin overlay mode */
317 #endif
318             /* ignore all of the motion commands */
319         case REQ_SCR_BCHAR:     /* FALLTHRU */
320         case REQ_SCR_BHPAGE:    /* FALLTHRU */
321         case REQ_SCR_BLINE:     /* FALLTHRU */
322         case REQ_SCR_BPAGE:     /* FALLTHRU */
323         case REQ_SCR_FCHAR:     /* FALLTHRU */
324         case REQ_SCR_FHPAGE:    /* FALLTHRU */
325         case REQ_SCR_FLINE:     /* FALLTHRU */
326         case REQ_SCR_FPAGE:     /* FALLTHRU */
327         case REQ_SCR_HBHALF:    /* FALLTHRU */
328         case REQ_SCR_HBLINE:    /* FALLTHRU */
329         case REQ_SCR_HFHALF:    /* FALLTHRU */
330         case REQ_SCR_HFLINE:    /* FALLTHRU */
331         case REQ_BEG_FIELD:     /* FALLTHRU */
332         case REQ_BEG_LINE:      /* FALLTHRU */
333         case REQ_DOWN_CHAR:     /* FALLTHRU */
334         case REQ_DOWN_FIELD:    /* FALLTHRU */
335         case REQ_END_FIELD:     /* FALLTHRU */
336         case REQ_END_LINE:      /* FALLTHRU */
337         case REQ_FIRST_FIELD:   /* FALLTHRU */
338         case REQ_FIRST_PAGE:    /* FALLTHRU */
339         case REQ_LAST_FIELD:    /* FALLTHRU */
340         case REQ_LAST_PAGE:     /* FALLTHRU */
341         case REQ_LEFT_CHAR:     /* FALLTHRU */
342         case REQ_LEFT_FIELD:    /* FALLTHRU */
343         case REQ_NEXT_CHAR:     /* FALLTHRU */
344         case REQ_NEXT_CHOICE:   /* FALLTHRU */
345         case REQ_NEXT_FIELD:    /* FALLTHRU */
346         case REQ_NEXT_LINE:     /* FALLTHRU */
347         case REQ_NEXT_PAGE:     /* FALLTHRU */
348         case REQ_NEXT_WORD:     /* FALLTHRU */
349         case REQ_PREV_CHAR:     /* FALLTHRU */
350         case REQ_PREV_CHOICE:   /* FALLTHRU */
351         case REQ_PREV_FIELD:    /* FALLTHRU */
352         case REQ_PREV_LINE:     /* FALLTHRU */
353         case REQ_PREV_PAGE:     /* FALLTHRU */
354         case REQ_PREV_WORD:     /* FALLTHRU */
355         case REQ_RIGHT_CHAR:    /* FALLTHRU */
356         case REQ_RIGHT_FIELD:   /* FALLTHRU */
357         case REQ_SFIRST_FIELD:  /* FALLTHRU */
358         case REQ_SLAST_FIELD:   /* FALLTHRU */
359         case REQ_SNEXT_FIELD:   /* FALLTHRU */
360         case REQ_SPREV_FIELD:   /* FALLTHRU */
361         case REQ_UP_CHAR:       /* FALLTHRU */
362         case REQ_UP_FIELD:      /* FALLTHRU */
363         case REQ_VALIDATION:    /* FALLTHRU */
364             modified = FALSE;
365             break;
366
367         default:
368             modified = FALSE;
369             if (ch >= MIN_FORM_COMMAND) {
370                 beep();
371             } else if (isprint(ch)) {
372                 modified = TRUE;
373             }
374             break;
375         }
376
377         /*
378          * If we do not force a re-validation, then field_buffer 0 will
379          * be lagging by one character.
380          */
381         if (modified && form_driver(form, REQ_VALIDATION) == E_OK && *result
382             < MIN_FORM_COMMAND)
383             ++length;
384
385         sprintf(lengths, "%d", length);
386         set_field_buffer(before, 1, lengths);
387     }
388
389     if ((after = current_field(form)) != before)
390         set_field_back(before, A_UNDERLINE);
391     return status;
392 }
393 #else
394
395 extern void no_edit_field(void);
396
397 void
398 no_edit_field(void)
399 {
400 }
401
402 #endif