1/****************************************************************************
2 * Copyright (c) 2003-2006,2008 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.14 2008/10/18 20:40:20 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 result available as a null-terminated string
33 * 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
44static 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
172static WINDOW *old_window;
173
174static void
175begin_popup(void)
176{
177    doupdate();
178    old_window = dupwin(curscr);
179}
180
181static void
182end_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 */
193void
194help_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
287static int
288offset_in_field(FORM * form)
289{
290    FIELD *field = current_field(form);
291    return form->curcol + form->currow * field->dcols;
292}
293
294static void
295inactive_field(FIELD * f)
296{
297    void *ptr = field_userptr(f);
298    set_field_back(f, (chtype) ptr);
299}
300
301int
302edit_field(FORM * form, int *result)
303{
304    int ch = wgetch(form_win(form));
305    int status;
306    FIELD *before;
307    unsigned n;
308    char lengths[80];
309    int length;
310    char *buffer;
311    int before_row = form->currow;
312    int before_col = form->curcol;
313    int before_off = offset_in_field(form);
314
315    before = current_field(form);
316    set_field_back(before, A_NORMAL);
317    if (ch <= KEY_MAX) {
318	set_field_back(before, A_REVERSE);
319    } else if (ch <= MAX_FORM_COMMAND) {
320	inactive_field(before);
321    }
322
323    *result = ch;
324    for (n = 0; n < SIZEOF(commands); ++n) {
325	if (commands[n].code == ch) {
326	    *result = commands[n].result;
327	    break;
328	}
329    }
330
331    status = form_driver(form, *result);
332
333    if (status == E_OK) {
334	bool modified = TRUE;
335
336	length = 0;
337	if ((buffer = field_buffer(before, 1)) != 0)
338	    length = atoi(buffer);
339	if (length < before_off)
340	    length = before_off;
341	switch (*result) {
342	case REQ_CLR_EOF:
343	    length = before_off;
344	    break;
345	case REQ_CLR_EOL:
346	    if (before_row + 1 == before->rows)
347		length = before_off;
348	    break;
349	case REQ_CLR_FIELD:
350	    length = 0;
351	    break;
352	case REQ_DEL_CHAR:
353	    if (length > before_off)
354		--length;
355	    break;
356	case REQ_DEL_PREV:
357	    if (length > 0) {
358		if (before_col > 0) {
359		    --length;
360		} else if (before_row > 0) {
361		    length -= before->cols + before_col;
362		}
363	    }
364	    break;
365	case REQ_NEW_LINE:
366	    length += before->cols;
367	    break;
368#if 0
369	    /* FIXME: finish these */
370	case REQ_DEL_LINE:	/* delete line */
371	case REQ_DEL_WORD:	/* delete word at cursor */
372	case REQ_INS_CHAR:	/* insert blank char at cursor */
373	case REQ_INS_LINE:	/* insert blank line at cursor */
374	case REQ_INS_MODE:	/* begin insert mode */
375	case REQ_OVL_MODE:	/* begin overlay mode */
376#endif
377	    /* ignore all of the motion commands */
378	case REQ_SCR_BCHAR:	/* FALLTHRU */
379	case REQ_SCR_BHPAGE:	/* FALLTHRU */
380	case REQ_SCR_BLINE:	/* FALLTHRU */
381	case REQ_SCR_BPAGE:	/* FALLTHRU */
382	case REQ_SCR_FCHAR:	/* FALLTHRU */
383	case REQ_SCR_FHPAGE:	/* FALLTHRU */
384	case REQ_SCR_FLINE:	/* FALLTHRU */
385	case REQ_SCR_FPAGE:	/* FALLTHRU */
386	case REQ_SCR_HBHALF:	/* FALLTHRU */
387	case REQ_SCR_HBLINE:	/* FALLTHRU */
388	case REQ_SCR_HFHALF:	/* FALLTHRU */
389	case REQ_SCR_HFLINE:	/* FALLTHRU */
390	case REQ_BEG_FIELD:	/* FALLTHRU */
391	case REQ_BEG_LINE:	/* FALLTHRU */
392	case REQ_DOWN_CHAR:	/* FALLTHRU */
393	case REQ_DOWN_FIELD:	/* FALLTHRU */
394	case REQ_END_FIELD:	/* FALLTHRU */
395	case REQ_END_LINE:	/* FALLTHRU */
396	case REQ_FIRST_FIELD:	/* FALLTHRU */
397	case REQ_FIRST_PAGE:	/* FALLTHRU */
398	case REQ_LAST_FIELD:	/* FALLTHRU */
399	case REQ_LAST_PAGE:	/* FALLTHRU */
400	case REQ_LEFT_CHAR:	/* FALLTHRU */
401	case REQ_LEFT_FIELD:	/* FALLTHRU */
402	case REQ_NEXT_CHAR:	/* FALLTHRU */
403	case REQ_NEXT_CHOICE:	/* FALLTHRU */
404	case REQ_NEXT_FIELD:	/* FALLTHRU */
405	case REQ_NEXT_LINE:	/* FALLTHRU */
406	case REQ_NEXT_PAGE:	/* FALLTHRU */
407	case REQ_NEXT_WORD:	/* FALLTHRU */
408	case REQ_PREV_CHAR:	/* FALLTHRU */
409	case REQ_PREV_CHOICE:	/* FALLTHRU */
410	case REQ_PREV_FIELD:	/* FALLTHRU */
411	case REQ_PREV_LINE:	/* FALLTHRU */
412	case REQ_PREV_PAGE:	/* FALLTHRU */
413	case REQ_PREV_WORD:	/* FALLTHRU */
414	case REQ_RIGHT_CHAR:	/* FALLTHRU */
415	case REQ_RIGHT_FIELD:	/* FALLTHRU */
416	case REQ_SFIRST_FIELD:	/* FALLTHRU */
417	case REQ_SLAST_FIELD:	/* FALLTHRU */
418	case REQ_SNEXT_FIELD:	/* FALLTHRU */
419	case REQ_SPREV_FIELD:	/* FALLTHRU */
420	case REQ_UP_CHAR:	/* FALLTHRU */
421	case REQ_UP_FIELD:	/* FALLTHRU */
422	case REQ_VALIDATION:	/* FALLTHRU */
423	    modified = FALSE;
424	    break;
425
426	default:
427	    modified = FALSE;
428	    if (ch >= MIN_FORM_COMMAND) {
429		beep();
430	    } else if (isprint(ch)) {
431		modified = TRUE;
432	    }
433	    break;
434	}
435
436	/*
437	 * If we do not force a re-validation, then field_buffer 0 will
438	 * be lagging by one character.
439	 */
440	if (modified && form_driver(form, REQ_VALIDATION) == E_OK && *result
441	    < MIN_FORM_COMMAND)
442	    ++length;
443
444	sprintf(lengths, "%d", length);
445	set_field_buffer(before, 1, lengths);
446    }
447
448    if (current_field(form) != before)
449	inactive_field(before);
450    return status;
451}
452#else
453
454extern void no_edit_field(void);
455
456void
457no_edit_field(void)
458{
459}
460
461#endif
462