ncurses 5.5
[ncurses.git] / test / edit_field.c
1 /*
2  * $Id: edit_field.c,v 1.11 2005/10/01 16:21:55 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 static struct {
18     int code;
19     int result;
20     const char *help;
21 } commands[] = {
22
23     {
24         CTRL('A'), REQ_NEXT_CHOICE, ""
25     },
26     {
27         CTRL('B'), REQ_PREV_WORD, "go to previous word"
28     },
29     {
30         CTRL('C'), REQ_CLR_EOL, "clear to end of line"
31     },
32     {
33         CTRL('D'), REQ_DOWN_FIELD, "move downward to field"
34     },
35     {
36         CTRL('E'), REQ_END_FIELD, "go to end of field"
37     },
38     {
39         CTRL('F'), REQ_NEXT_PAGE, "go to next page"
40     },
41     {
42         CTRL('G'), REQ_DEL_WORD, "delete current word"
43     },
44     {
45         CTRL('H'), REQ_DEL_PREV, "delete previous character"
46     },
47     {
48         CTRL('I'), REQ_INS_CHAR, "insert character"
49     },
50     {
51         CTRL('K'), REQ_CLR_EOF, "clear to end of field"
52     },
53     {
54         CTRL('L'), REQ_LEFT_FIELD, "go to field to left"
55     },
56     {
57         CTRL('M'), REQ_NEW_LINE, "insert/overlay new line"
58     },
59     {
60         CTRL('N'), REQ_NEXT_FIELD, "go to next field"
61     },
62     {
63         CTRL('O'), REQ_INS_LINE, "insert blank line at cursor"
64     },
65     {
66         CTRL('P'), REQ_PREV_FIELD, "go to previous field"
67     },
68     {
69         CTRL('Q'), MY_QUIT, "exit form"
70     },
71     {
72         CTRL('R'), REQ_RIGHT_FIELD, "go to field to right"
73     },
74     {
75         CTRL('S'), REQ_BEG_FIELD, "go to beginning of field"
76     },
77     {
78         CTRL('T'), MY_EDT_MODE, "toggle O_EDIT mode, clear field status",
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), MY_HELP, "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 edit/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 KEY_HOME:
201             y1 = 0;
202             break;
203         case KEY_END:
204             y1 = y2;
205             break;
206         case KEY_PREVIOUS:
207         case KEY_PPAGE:
208             if (y1 > 0) {
209                 y1 -= high / 2;
210                 if (y1 < 0)
211                     y1 = 0;
212             } else {
213                 beep();
214             }
215             break;
216         case KEY_NEXT:
217         case KEY_NPAGE:
218             if (y1 < y2) {
219                 y1 += high / 2;
220                 if (y1 >= y2)
221                     y1 = y2;
222             } else {
223                 beep();
224             }
225             break;
226         case CTRL('P'):
227         case KEY_UP:
228             if (y1 > 0)
229                 --y1;
230             else
231                 beep();
232             break;
233         case CTRL('N'):
234         case KEY_DOWN:
235             if (y1 < y2)
236                 ++y1;
237             else
238                 beep();
239             break;
240         default:
241             beep();
242             break;
243         case ERR:
244             break;
245         }
246         werase(help);
247         box(help, 0, 0);
248         wnoutrefresh(help);
249         pnoutrefresh(data, y1, 0, y0 + 1, x0 + 1, high, wide);
250         doupdate();
251     } while ((ch = wgetch(data)) != ERR && ch != QUIT && ch != ESCAPE);
252     werase(help);
253     wrefresh(help);
254     delwin(help);
255     delwin(data);
256
257     end_popup();
258 }
259
260 static int
261 offset_in_field(FORM * form)
262 {
263     FIELD *field = current_field(form);
264     return form->curcol + form->currow * field->dcols;
265 }
266
267 int
268 edit_field(FORM * form, int *result)
269 {
270     int ch = wgetch(form_win(form));
271     int status;
272     FIELD *before;
273     FIELD *after;
274     unsigned n;
275     char lengths[80];
276     int length;
277     char *buffer;
278     int before_row = form->currow;
279     int before_col = form->curcol;
280     int before_off = offset_in_field(form);
281
282     before = current_field(form);
283     set_field_back(before, A_NORMAL);
284     if (ch <= KEY_MAX) {
285         set_field_back(before, A_REVERSE);
286     } else if (ch <= MAX_FORM_COMMAND) {
287         set_field_back(before, A_UNDERLINE);
288     }
289
290     *result = ch;
291     for (n = 0; n < SIZEOF(commands); ++n) {
292         if (commands[n].code == ch) {
293             *result = commands[n].result;
294             break;
295         }
296     }
297
298     status = form_driver(form, *result);
299
300     if (status == E_OK) {
301         bool modified = TRUE;
302
303         length = 0;
304         if ((buffer = field_buffer(before, 1)) != 0)
305             length = atoi(buffer);
306         if (length < before_off)
307             length = before_off;
308         switch (*result) {
309         case REQ_CLR_EOF:
310             length = before_off;
311             break;
312         case REQ_CLR_EOL:
313             if (before_row + 1 == before->rows)
314                 length = before_off;
315             break;
316         case REQ_CLR_FIELD:
317             length = 0;
318             break;
319         case REQ_DEL_CHAR:
320             if (length > before_off)
321                 --length;
322             break;
323         case REQ_DEL_PREV:
324             if (length > 0) {
325                 if (before_col > 0) {
326                     --length;
327                 } else if (before_row > 0) {
328                     length -= before->cols + before_col;
329                 }
330             }
331             break;
332         case REQ_NEW_LINE:
333             length += before->cols;
334             break;
335 #if 0
336             /* FIXME: finish these */
337         case REQ_DEL_LINE:      /* delete line */
338         case REQ_DEL_WORD:      /* delete word at cursor */
339         case REQ_INS_CHAR:      /* insert blank char at cursor */
340         case REQ_INS_LINE:      /* insert blank line at cursor */
341         case REQ_INS_MODE:      /* begin insert mode */
342         case REQ_OVL_MODE:      /* begin overlay mode */
343 #endif
344             /* ignore all of the motion commands */
345         case REQ_SCR_BCHAR:     /* FALLTHRU */
346         case REQ_SCR_BHPAGE:    /* FALLTHRU */
347         case REQ_SCR_BLINE:     /* FALLTHRU */
348         case REQ_SCR_BPAGE:     /* FALLTHRU */
349         case REQ_SCR_FCHAR:     /* FALLTHRU */
350         case REQ_SCR_FHPAGE:    /* FALLTHRU */
351         case REQ_SCR_FLINE:     /* FALLTHRU */
352         case REQ_SCR_FPAGE:     /* FALLTHRU */
353         case REQ_SCR_HBHALF:    /* FALLTHRU */
354         case REQ_SCR_HBLINE:    /* FALLTHRU */
355         case REQ_SCR_HFHALF:    /* FALLTHRU */
356         case REQ_SCR_HFLINE:    /* FALLTHRU */
357         case REQ_BEG_FIELD:     /* FALLTHRU */
358         case REQ_BEG_LINE:      /* FALLTHRU */
359         case REQ_DOWN_CHAR:     /* FALLTHRU */
360         case REQ_DOWN_FIELD:    /* FALLTHRU */
361         case REQ_END_FIELD:     /* FALLTHRU */
362         case REQ_END_LINE:      /* FALLTHRU */
363         case REQ_FIRST_FIELD:   /* FALLTHRU */
364         case REQ_FIRST_PAGE:    /* FALLTHRU */
365         case REQ_LAST_FIELD:    /* FALLTHRU */
366         case REQ_LAST_PAGE:     /* FALLTHRU */
367         case REQ_LEFT_CHAR:     /* FALLTHRU */
368         case REQ_LEFT_FIELD:    /* FALLTHRU */
369         case REQ_NEXT_CHAR:     /* FALLTHRU */
370         case REQ_NEXT_CHOICE:   /* FALLTHRU */
371         case REQ_NEXT_FIELD:    /* FALLTHRU */
372         case REQ_NEXT_LINE:     /* FALLTHRU */
373         case REQ_NEXT_PAGE:     /* FALLTHRU */
374         case REQ_NEXT_WORD:     /* FALLTHRU */
375         case REQ_PREV_CHAR:     /* FALLTHRU */
376         case REQ_PREV_CHOICE:   /* FALLTHRU */
377         case REQ_PREV_FIELD:    /* FALLTHRU */
378         case REQ_PREV_LINE:     /* FALLTHRU */
379         case REQ_PREV_PAGE:     /* FALLTHRU */
380         case REQ_PREV_WORD:     /* FALLTHRU */
381         case REQ_RIGHT_CHAR:    /* FALLTHRU */
382         case REQ_RIGHT_FIELD:   /* FALLTHRU */
383         case REQ_SFIRST_FIELD:  /* FALLTHRU */
384         case REQ_SLAST_FIELD:   /* FALLTHRU */
385         case REQ_SNEXT_FIELD:   /* FALLTHRU */
386         case REQ_SPREV_FIELD:   /* FALLTHRU */
387         case REQ_UP_CHAR:       /* FALLTHRU */
388         case REQ_UP_FIELD:      /* FALLTHRU */
389         case REQ_VALIDATION:    /* FALLTHRU */
390             modified = FALSE;
391             break;
392
393         default:
394             modified = FALSE;
395             if (ch >= MIN_FORM_COMMAND) {
396                 beep();
397             } else if (isprint(ch)) {
398                 modified = TRUE;
399             }
400             break;
401         }
402
403         /*
404          * If we do not force a re-validation, then field_buffer 0 will
405          * be lagging by one character.
406          */
407         if (modified && form_driver(form, REQ_VALIDATION) == E_OK && *result
408             < MIN_FORM_COMMAND)
409             ++length;
410
411         sprintf(lengths, "%d", length);
412         set_field_buffer(before, 1, lengths);
413     }
414
415     if ((after = current_field(form)) != before)
416         set_field_back(before, A_UNDERLINE);
417     return status;
418 }
419 #else
420
421 extern void no_edit_field(void);
422
423 void
424 no_edit_field(void)
425 {
426 }
427
428 #endif