1217309Snwhitehorn/*
2251843Sbapt *  $Id: dlg_keys.c,v 1.34 2011/10/14 00:41:08 tom Exp $
3217309Snwhitehorn *
4251843Sbapt *  dlg_keys.c -- runtime binding support for dialog
5217309Snwhitehorn *
6251843Sbapt *  Copyright 2006-2009,2011 Thomas E. Dickey
7217309Snwhitehorn *
8217309Snwhitehorn *  This program is free software; you can redistribute it and/or modify
9217309Snwhitehorn *  it under the terms of the GNU Lesser General Public License, version 2.1
10217309Snwhitehorn *  as published by the Free Software Foundation.
11217309Snwhitehorn *
12217309Snwhitehorn *  This program is distributed in the hope that it will be useful, but
13217309Snwhitehorn *  WITHOUT ANY WARRANTY; without even the implied warranty of
14217309Snwhitehorn *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15217309Snwhitehorn *  Lesser General Public License for more details.
16217309Snwhitehorn *
17217309Snwhitehorn *  You should have received a copy of the GNU Lesser General Public
18217309Snwhitehorn *  License along with this program; if not, write to
19217309Snwhitehorn *	Free Software Foundation, Inc.
20217309Snwhitehorn *	51 Franklin St., Fifth Floor
21217309Snwhitehorn *	Boston, MA 02110, USA.
22217309Snwhitehorn */
23217309Snwhitehorn
24217309Snwhitehorn#include <dialog.h>
25217309Snwhitehorn#include <dlg_keys.h>
26217309Snwhitehorn
27217309Snwhitehorn#define LIST_BINDINGS struct _list_bindings
28217309Snwhitehorn
29217309SnwhitehornLIST_BINDINGS {
30217309Snwhitehorn    LIST_BINDINGS *link;
31217309Snwhitehorn    WINDOW *win;		/* window on which widget gets input */
32217309Snwhitehorn    const char *name;		/* widget name */
33217309Snwhitehorn    bool buttons;		/* true only for dlg_register_buttons() */
34217309Snwhitehorn    DLG_KEYS_BINDING *binding;	/* list of bindings */
35217309Snwhitehorn};
36217309Snwhitehorn
37251843Sbapt#define WILDNAME "*"
38217309Snwhitehornstatic LIST_BINDINGS *all_bindings;
39217309Snwhitehornstatic const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING;
40217309Snwhitehorn
41217309Snwhitehorn/*
42217309Snwhitehorn * For a given named widget's window, associate a binding table.
43217309Snwhitehorn */
44217309Snwhitehornvoid
45217309Snwhitehorndlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding)
46217309Snwhitehorn{
47217309Snwhitehorn    LIST_BINDINGS *p, *q;
48217309Snwhitehorn
49217309Snwhitehorn    for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
50217309Snwhitehorn	if (p->win == win && !strcmp(p->name, name)) {
51217309Snwhitehorn	    p->binding = binding;
52217309Snwhitehorn	    return;
53217309Snwhitehorn	}
54217309Snwhitehorn    }
55217309Snwhitehorn    /* add built-in bindings at the end of the list (see compare_bindings). */
56217309Snwhitehorn    if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
57217309Snwhitehorn	p->win = win;
58217309Snwhitehorn	p->name = name;
59217309Snwhitehorn	p->binding = binding;
60217309Snwhitehorn	if (q != 0)
61217309Snwhitehorn	    q->link = p;
62217309Snwhitehorn	else
63217309Snwhitehorn	    all_bindings = p;
64217309Snwhitehorn    }
65251843Sbapt#if defined(HAVE_DLG_TRACE) && defined(HAVE_RC_FILE)
66251843Sbapt    /*
67251843Sbapt     * Trace the binding information assigned to this window.  For most widgets
68251843Sbapt     * there is only one binding table.  forms have two, so the trace will be
69251843Sbapt     * longer.  Since compiled-in bindings are only visible when the widget is
70251843Sbapt     * registered, there is no other way to see what bindings are available,
71251843Sbapt     * than by running dialog and tracing it.
72251843Sbapt     */
73251843Sbapt    dlg_trace_msg("# dlg_register_window %s\n", name);
74251843Sbapt    dlg_dump_window_keys(dialog_state.trace_output, win);
75251843Sbapt#endif
76217309Snwhitehorn}
77217309Snwhitehorn
78217309Snwhitehorn/*
79217309Snwhitehorn * Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file
80217309Snwhitehorn * definitions, depending on whether 'win' is null.
81217309Snwhitehorn */
82217309Snwhitehornstatic int
83217309Snwhitehornkey_is_bound(WINDOW *win, const char *name, int curses_key, int function_key)
84217309Snwhitehorn{
85217309Snwhitehorn    LIST_BINDINGS *p;
86217309Snwhitehorn
87217309Snwhitehorn    for (p = all_bindings; p != 0; p = p->link) {
88217309Snwhitehorn	if (p->win == win && !dlg_strcmp(p->name, name)) {
89217309Snwhitehorn	    int n;
90217309Snwhitehorn	    for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
91217309Snwhitehorn		if (p->binding[n].curses_key == curses_key
92217309Snwhitehorn		    && p->binding[n].is_function_key == function_key) {
93217309Snwhitehorn		    return TRUE;
94217309Snwhitehorn		}
95217309Snwhitehorn	    }
96217309Snwhitehorn	}
97217309Snwhitehorn    }
98217309Snwhitehorn    return FALSE;
99217309Snwhitehorn}
100217309Snwhitehorn
101217309Snwhitehorn/*
102217309Snwhitehorn * Call this function after dlg_register_window(), for the list of button
103217309Snwhitehorn * labels associated with the widget.
104217309Snwhitehorn *
105217309Snwhitehorn * Ensure that dlg_lookup_key() will not accidentally translate a key that
106217309Snwhitehorn * we would like to use for a button abbreviation to some other key, e.g.,
107217309Snwhitehorn * h/j/k/l for navigation into a cursor key.  Do this by binding the key
108217309Snwhitehorn * to itself.
109217309Snwhitehorn *
110217309Snwhitehorn * See dlg_char_to_button().
111217309Snwhitehorn */
112217309Snwhitehornvoid
113217309Snwhitehorndlg_register_buttons(WINDOW *win, const char *name, const char **buttons)
114217309Snwhitehorn{
115217309Snwhitehorn    int n;
116217309Snwhitehorn    LIST_BINDINGS *p;
117217309Snwhitehorn    DLG_KEYS_BINDING *q;
118217309Snwhitehorn
119217309Snwhitehorn    if (buttons == 0)
120217309Snwhitehorn	return;
121217309Snwhitehorn
122217309Snwhitehorn    for (n = 0; buttons[n] != 0; ++n) {
123217309Snwhitehorn	int curses_key = dlg_button_to_char(buttons[n]);
124217309Snwhitehorn
125217309Snwhitehorn	/* ignore multibyte characters */
126217309Snwhitehorn	if (curses_key >= KEY_MIN)
127217309Snwhitehorn	    continue;
128217309Snwhitehorn
129217309Snwhitehorn	/* if it is not bound in the widget, skip it (no conflicts) */
130217309Snwhitehorn	if (!key_is_bound(win, name, curses_key, FALSE))
131217309Snwhitehorn	    continue;
132217309Snwhitehorn
133217309Snwhitehorn#ifdef HAVE_RC_FILE
134217309Snwhitehorn	/* if it is bound in the rc-file, skip it */
135217309Snwhitehorn	if (key_is_bound(0, name, curses_key, FALSE))
136217309Snwhitehorn	    continue;
137217309Snwhitehorn#endif
138217309Snwhitehorn
139217309Snwhitehorn	if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
140217309Snwhitehorn	    if ((q = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0) {
141217309Snwhitehorn		q[0].is_function_key = 0;
142217309Snwhitehorn		q[0].curses_key = curses_key;
143217309Snwhitehorn		q[0].dialog_key = curses_key;
144217309Snwhitehorn		q[1] = end_keys_binding;
145217309Snwhitehorn
146217309Snwhitehorn		p->win = win;
147217309Snwhitehorn		p->name = name;
148217309Snwhitehorn		p->buttons = TRUE;
149217309Snwhitehorn		p->binding = q;
150217309Snwhitehorn
151217309Snwhitehorn		/* put these at the beginning, to override the widget's table */
152217309Snwhitehorn		p->link = all_bindings;
153217309Snwhitehorn		all_bindings = p;
154217309Snwhitehorn	    } else {
155217309Snwhitehorn		free(p);
156217309Snwhitehorn	    }
157217309Snwhitehorn	}
158217309Snwhitehorn    }
159217309Snwhitehorn}
160217309Snwhitehorn
161217309Snwhitehorn/*
162217309Snwhitehorn * Remove the bindings for a given window.
163217309Snwhitehorn */
164217309Snwhitehornvoid
165217309Snwhitehorndlg_unregister_window(WINDOW *win)
166217309Snwhitehorn{
167217309Snwhitehorn    LIST_BINDINGS *p, *q;
168217309Snwhitehorn
169217309Snwhitehorn    for (p = all_bindings, q = 0; p != 0; p = p->link) {
170217309Snwhitehorn	if (p->win == win) {
171217309Snwhitehorn	    if (q != 0) {
172217309Snwhitehorn		q->link = p->link;
173217309Snwhitehorn	    } else {
174217309Snwhitehorn		all_bindings = p->link;
175217309Snwhitehorn	    }
176217309Snwhitehorn	    /* the user-defined and buttons-bindings all are length=1 */
177217309Snwhitehorn	    if (p->binding[1].is_function_key < 0)
178217309Snwhitehorn		free(p->binding);
179217309Snwhitehorn	    free(p);
180217309Snwhitehorn	    dlg_unregister_window(win);
181217309Snwhitehorn	    break;
182217309Snwhitehorn	}
183217309Snwhitehorn	q = p;
184217309Snwhitehorn    }
185217309Snwhitehorn}
186217309Snwhitehorn
187217309Snwhitehorn/*
188217309Snwhitehorn * Call this after wgetch(), using the same window pointer and passing
189217309Snwhitehorn * the curses-key.
190217309Snwhitehorn *
191217309Snwhitehorn * If there is no binding associated with the widget, it simply returns
192217309Snwhitehorn * the given curses-key.
193217309Snwhitehorn *
194217309Snwhitehorn * Parameters:
195217309Snwhitehorn *	win is the window on which the wgetch() was done.
196217309Snwhitehorn *	curses_key is the value returned by wgetch().
197217309Snwhitehorn *	fkey in/out (on input, it is true if curses_key is a function key,
198217309Snwhitehorn *		and on output, it is true if the result is a function key).
199217309Snwhitehorn */
200217309Snwhitehornint
201217309Snwhitehorndlg_lookup_key(WINDOW *win, int curses_key, int *fkey)
202217309Snwhitehorn{
203217309Snwhitehorn    LIST_BINDINGS *p;
204251843Sbapt    DLG_KEYS_BINDING *q;
205217309Snwhitehorn
206217309Snwhitehorn    /*
207217309Snwhitehorn     * Ignore mouse clicks, since they are already encoded properly.
208217309Snwhitehorn     */
209217309Snwhitehorn#ifdef KEY_MOUSE
210217309Snwhitehorn    if (*fkey != 0 && curses_key == KEY_MOUSE) {
211217309Snwhitehorn	;
212217309Snwhitehorn    } else
213217309Snwhitehorn#endif
214217309Snwhitehorn	/*
215217309Snwhitehorn	 * Ignore resize events, since they are already encoded properly.
216217309Snwhitehorn	 */
217217309Snwhitehorn#ifdef KEY_RESIZE
218217309Snwhitehorn    if (*fkey != 0 && curses_key == KEY_RESIZE) {
219217309Snwhitehorn	;
220217309Snwhitehorn    } else
221217309Snwhitehorn#endif
222217309Snwhitehorn    if (*fkey == 0 || curses_key < KEY_MAX) {
223251843Sbapt	const char *name = WILDNAME;
224251843Sbapt	if (win != 0) {
225251843Sbapt	    for (p = all_bindings; p != 0; p = p->link) {
226251843Sbapt		if (p->win == win) {
227251843Sbapt		    name = p->name;
228251843Sbapt		    break;
229251843Sbapt		}
230251843Sbapt	    }
231251843Sbapt	}
232217309Snwhitehorn	for (p = all_bindings; p != 0; p = p->link) {
233251843Sbapt	    if (p->win == win || (p->win == 0 && !strcmp(p->name, name))) {
234217309Snwhitehorn		int function_key = (*fkey != 0);
235251843Sbapt		for (q = p->binding; q->is_function_key >= 0; ++q) {
236217309Snwhitehorn		    if (p->buttons
237217309Snwhitehorn			&& !function_key
238251843Sbapt			&& q->curses_key == (int) dlg_toupper(curses_key)) {
239217309Snwhitehorn			*fkey = 0;
240251843Sbapt			return q->dialog_key;
241217309Snwhitehorn		    }
242251843Sbapt		    if (q->curses_key == curses_key
243251843Sbapt			&& q->is_function_key == function_key) {
244251843Sbapt			*fkey = q->dialog_key;
245217309Snwhitehorn			return *fkey;
246217309Snwhitehorn		    }
247217309Snwhitehorn		}
248217309Snwhitehorn	    }
249217309Snwhitehorn	}
250217309Snwhitehorn    }
251217309Snwhitehorn    return curses_key;
252217309Snwhitehorn}
253217309Snwhitehorn
254217309Snwhitehorn/*
255217309Snwhitehorn * Test a dialog internal keycode to see if it corresponds to one of the push
256217309Snwhitehorn * buttons on the widget such as "OK".
257217309Snwhitehorn *
258217309Snwhitehorn * This is only useful if there are user-defined key bindings, since there are
259217309Snwhitehorn * no built-in bindings that map directly to DLGK_OK, etc.
260217309Snwhitehorn *
261217309Snwhitehorn * See also dlg_ok_buttoncode().
262217309Snwhitehorn */
263217309Snwhitehornint
264217309Snwhitehorndlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
265217309Snwhitehorn{
266217309Snwhitehorn    int done = FALSE;
267217309Snwhitehorn
268217309Snwhitehorn#ifdef HAVE_RC_FILE
269217309Snwhitehorn    if (fkey) {
270217309Snwhitehorn	switch ((DLG_KEYS_ENUM) dialog_key) {
271217309Snwhitehorn	case DLGK_OK:
272217309Snwhitehorn	    *resultp = DLG_EXIT_OK;
273217309Snwhitehorn	    done = TRUE;
274217309Snwhitehorn	    break;
275217309Snwhitehorn	case DLGK_CANCEL:
276217309Snwhitehorn	    if (!dialog_vars.nocancel) {
277217309Snwhitehorn		*resultp = DLG_EXIT_CANCEL;
278217309Snwhitehorn		done = TRUE;
279217309Snwhitehorn	    }
280217309Snwhitehorn	    break;
281217309Snwhitehorn	case DLGK_EXTRA:
282217309Snwhitehorn	    if (dialog_vars.extra_button) {
283217309Snwhitehorn		*resultp = DLG_EXIT_EXTRA;
284217309Snwhitehorn		done = TRUE;
285217309Snwhitehorn	    }
286217309Snwhitehorn	    break;
287217309Snwhitehorn	case DLGK_HELP:
288217309Snwhitehorn	    if (dialog_vars.help_button) {
289217309Snwhitehorn		*resultp = DLG_EXIT_HELP;
290217309Snwhitehorn		done = TRUE;
291217309Snwhitehorn	    }
292217309Snwhitehorn	    break;
293217309Snwhitehorn	case DLGK_ESC:
294217309Snwhitehorn	    *resultp = DLG_EXIT_ESC;
295217309Snwhitehorn	    done = TRUE;
296217309Snwhitehorn	    break;
297217309Snwhitehorn	default:
298217309Snwhitehorn	    break;
299217309Snwhitehorn	}
300217309Snwhitehorn    } else
301217309Snwhitehorn#endif
302217309Snwhitehorn    if (dialog_key == ESC) {
303217309Snwhitehorn	*resultp = DLG_EXIT_ESC;
304217309Snwhitehorn	done = TRUE;
305217309Snwhitehorn    } else if (dialog_key == ERR) {
306217309Snwhitehorn	*resultp = DLG_EXIT_ERROR;
307217309Snwhitehorn	done = TRUE;
308217309Snwhitehorn    }
309217309Snwhitehorn
310217309Snwhitehorn    return done;
311217309Snwhitehorn}
312217309Snwhitehorn
313217309Snwhitehorn#ifdef HAVE_RC_FILE
314217309Snwhitehorntypedef struct {
315217309Snwhitehorn    const char *name;
316217309Snwhitehorn    int code;
317217309Snwhitehorn} CODENAME;
318217309Snwhitehorn
319251843Sbapt#define ASCII_NAME(name,code)  { #name, code }
320217309Snwhitehorn#define CURSES_NAME(upper) { #upper, KEY_ ## upper }
321217309Snwhitehorn#define COUNT_CURSES  sizeof(curses_names)/sizeof(curses_names[0])
322217309Snwhitehornstatic const CODENAME curses_names[] =
323217309Snwhitehorn{
324251843Sbapt    ASCII_NAME(ESC, '\033'),
325251843Sbapt    ASCII_NAME(CR, '\r'),
326251843Sbapt    ASCII_NAME(LF, '\n'),
327251843Sbapt    ASCII_NAME(FF, '\f'),
328251843Sbapt    ASCII_NAME(TAB, '\t'),
329251843Sbapt    ASCII_NAME(DEL, '\177'),
330251843Sbapt
331217309Snwhitehorn    CURSES_NAME(DOWN),
332217309Snwhitehorn    CURSES_NAME(UP),
333217309Snwhitehorn    CURSES_NAME(LEFT),
334217309Snwhitehorn    CURSES_NAME(RIGHT),
335217309Snwhitehorn    CURSES_NAME(HOME),
336217309Snwhitehorn    CURSES_NAME(BACKSPACE),
337217309Snwhitehorn    CURSES_NAME(F0),
338217309Snwhitehorn    CURSES_NAME(DL),
339217309Snwhitehorn    CURSES_NAME(IL),
340217309Snwhitehorn    CURSES_NAME(DC),
341217309Snwhitehorn    CURSES_NAME(IC),
342217309Snwhitehorn    CURSES_NAME(EIC),
343217309Snwhitehorn    CURSES_NAME(CLEAR),
344217309Snwhitehorn    CURSES_NAME(EOS),
345217309Snwhitehorn    CURSES_NAME(EOL),
346217309Snwhitehorn    CURSES_NAME(SF),
347217309Snwhitehorn    CURSES_NAME(SR),
348217309Snwhitehorn    CURSES_NAME(NPAGE),
349217309Snwhitehorn    CURSES_NAME(PPAGE),
350217309Snwhitehorn    CURSES_NAME(STAB),
351217309Snwhitehorn    CURSES_NAME(CTAB),
352217309Snwhitehorn    CURSES_NAME(CATAB),
353217309Snwhitehorn    CURSES_NAME(ENTER),
354217309Snwhitehorn    CURSES_NAME(PRINT),
355217309Snwhitehorn    CURSES_NAME(LL),
356217309Snwhitehorn    CURSES_NAME(A1),
357217309Snwhitehorn    CURSES_NAME(A3),
358217309Snwhitehorn    CURSES_NAME(B2),
359217309Snwhitehorn    CURSES_NAME(C1),
360217309Snwhitehorn    CURSES_NAME(C3),
361217309Snwhitehorn    CURSES_NAME(BTAB),
362217309Snwhitehorn    CURSES_NAME(BEG),
363217309Snwhitehorn    CURSES_NAME(CANCEL),
364217309Snwhitehorn    CURSES_NAME(CLOSE),
365217309Snwhitehorn    CURSES_NAME(COMMAND),
366217309Snwhitehorn    CURSES_NAME(COPY),
367217309Snwhitehorn    CURSES_NAME(CREATE),
368217309Snwhitehorn    CURSES_NAME(END),
369217309Snwhitehorn    CURSES_NAME(EXIT),
370217309Snwhitehorn    CURSES_NAME(FIND),
371217309Snwhitehorn    CURSES_NAME(HELP),
372217309Snwhitehorn    CURSES_NAME(MARK),
373217309Snwhitehorn    CURSES_NAME(MESSAGE),
374217309Snwhitehorn    CURSES_NAME(MOVE),
375217309Snwhitehorn    CURSES_NAME(NEXT),
376217309Snwhitehorn    CURSES_NAME(OPEN),
377217309Snwhitehorn    CURSES_NAME(OPTIONS),
378217309Snwhitehorn    CURSES_NAME(PREVIOUS),
379217309Snwhitehorn    CURSES_NAME(REDO),
380217309Snwhitehorn    CURSES_NAME(REFERENCE),
381217309Snwhitehorn    CURSES_NAME(REFRESH),
382217309Snwhitehorn    CURSES_NAME(REPLACE),
383217309Snwhitehorn    CURSES_NAME(RESTART),
384217309Snwhitehorn    CURSES_NAME(RESUME),
385217309Snwhitehorn    CURSES_NAME(SAVE),
386217309Snwhitehorn    CURSES_NAME(SBEG),
387217309Snwhitehorn    CURSES_NAME(SCANCEL),
388217309Snwhitehorn    CURSES_NAME(SCOMMAND),
389217309Snwhitehorn    CURSES_NAME(SCOPY),
390217309Snwhitehorn    CURSES_NAME(SCREATE),
391217309Snwhitehorn    CURSES_NAME(SDC),
392217309Snwhitehorn    CURSES_NAME(SDL),
393217309Snwhitehorn    CURSES_NAME(SELECT),
394217309Snwhitehorn    CURSES_NAME(SEND),
395217309Snwhitehorn    CURSES_NAME(SEOL),
396217309Snwhitehorn    CURSES_NAME(SEXIT),
397217309Snwhitehorn    CURSES_NAME(SFIND),
398217309Snwhitehorn    CURSES_NAME(SHELP),
399217309Snwhitehorn    CURSES_NAME(SHOME),
400217309Snwhitehorn    CURSES_NAME(SIC),
401217309Snwhitehorn    CURSES_NAME(SLEFT),
402217309Snwhitehorn    CURSES_NAME(SMESSAGE),
403217309Snwhitehorn    CURSES_NAME(SMOVE),
404217309Snwhitehorn    CURSES_NAME(SNEXT),
405217309Snwhitehorn    CURSES_NAME(SOPTIONS),
406217309Snwhitehorn    CURSES_NAME(SPREVIOUS),
407217309Snwhitehorn    CURSES_NAME(SPRINT),
408217309Snwhitehorn    CURSES_NAME(SREDO),
409217309Snwhitehorn    CURSES_NAME(SREPLACE),
410217309Snwhitehorn    CURSES_NAME(SRIGHT),
411217309Snwhitehorn    CURSES_NAME(SRSUME),
412217309Snwhitehorn    CURSES_NAME(SSAVE),
413217309Snwhitehorn    CURSES_NAME(SSUSPEND),
414217309Snwhitehorn    CURSES_NAME(SUNDO),
415217309Snwhitehorn    CURSES_NAME(SUSPEND),
416217309Snwhitehorn    CURSES_NAME(UNDO),
417217309Snwhitehorn};
418217309Snwhitehorn
419217309Snwhitehorn#define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
420217309Snwhitehorn#define COUNT_DIALOG  sizeof(dialog_names)/sizeof(dialog_names[0])
421217309Snwhitehornstatic const CODENAME dialog_names[] =
422217309Snwhitehorn{
423217309Snwhitehorn    DIALOG_NAME(OK),
424217309Snwhitehorn    DIALOG_NAME(CANCEL),
425217309Snwhitehorn    DIALOG_NAME(EXTRA),
426217309Snwhitehorn    DIALOG_NAME(HELP),
427217309Snwhitehorn    DIALOG_NAME(ESC),
428217309Snwhitehorn    DIALOG_NAME(PAGE_FIRST),
429217309Snwhitehorn    DIALOG_NAME(PAGE_LAST),
430217309Snwhitehorn    DIALOG_NAME(PAGE_NEXT),
431217309Snwhitehorn    DIALOG_NAME(PAGE_PREV),
432217309Snwhitehorn    DIALOG_NAME(ITEM_FIRST),
433217309Snwhitehorn    DIALOG_NAME(ITEM_LAST),
434217309Snwhitehorn    DIALOG_NAME(ITEM_NEXT),
435217309Snwhitehorn    DIALOG_NAME(ITEM_PREV),
436217309Snwhitehorn    DIALOG_NAME(FIELD_FIRST),
437217309Snwhitehorn    DIALOG_NAME(FIELD_LAST),
438217309Snwhitehorn    DIALOG_NAME(FIELD_NEXT),
439217309Snwhitehorn    DIALOG_NAME(FIELD_PREV),
440251843Sbapt    DIALOG_NAME(FORM_FIRST),
441251843Sbapt    DIALOG_NAME(FORM_LAST),
442251843Sbapt    DIALOG_NAME(FORM_NEXT),
443251843Sbapt    DIALOG_NAME(FORM_PREV),
444217309Snwhitehorn    DIALOG_NAME(GRID_UP),
445217309Snwhitehorn    DIALOG_NAME(GRID_DOWN),
446217309Snwhitehorn    DIALOG_NAME(GRID_LEFT),
447217309Snwhitehorn    DIALOG_NAME(GRID_RIGHT),
448217309Snwhitehorn    DIALOG_NAME(DELETE_LEFT),
449217309Snwhitehorn    DIALOG_NAME(DELETE_RIGHT),
450217309Snwhitehorn    DIALOG_NAME(DELETE_ALL),
451217309Snwhitehorn    DIALOG_NAME(ENTER),
452217309Snwhitehorn    DIALOG_NAME(BEGIN),
453217309Snwhitehorn    DIALOG_NAME(FINAL),
454251843Sbapt    DIALOG_NAME(SELECT),
455251843Sbapt    DIALOG_NAME(HELPFILE),
456251843Sbapt    DIALOG_NAME(TRACE)
457217309Snwhitehorn};
458217309Snwhitehorn
459217309Snwhitehornstatic char *
460217309Snwhitehornskip_white(char *s)
461217309Snwhitehorn{
462217309Snwhitehorn    while (*s != '\0' && isspace(UCH(*s)))
463217309Snwhitehorn	++s;
464217309Snwhitehorn    return s;
465217309Snwhitehorn}
466217309Snwhitehorn
467217309Snwhitehornstatic char *
468217309Snwhitehornskip_black(char *s)
469217309Snwhitehorn{
470217309Snwhitehorn    while (*s != '\0' && !isspace(UCH(*s)))
471217309Snwhitehorn	++s;
472217309Snwhitehorn    return s;
473217309Snwhitehorn}
474217309Snwhitehorn
475217309Snwhitehorn/*
476217309Snwhitehorn * Find a user-defined binding, given the curses key code.
477217309Snwhitehorn */
478217309Snwhitehornstatic DLG_KEYS_BINDING *
479217309Snwhitehornfind_binding(char *widget, int curses_key)
480217309Snwhitehorn{
481217309Snwhitehorn    LIST_BINDINGS *p;
482217309Snwhitehorn    DLG_KEYS_BINDING *result = 0;
483217309Snwhitehorn
484217309Snwhitehorn    for (p = all_bindings; p != 0; p = p->link) {
485217309Snwhitehorn	if (p->win == 0
486217309Snwhitehorn	    && !dlg_strcmp(p->name, widget)
487217309Snwhitehorn	    && p->binding->curses_key == curses_key) {
488217309Snwhitehorn	    result = p->binding;
489217309Snwhitehorn	    break;
490217309Snwhitehorn	}
491217309Snwhitehorn    }
492217309Snwhitehorn    return result;
493217309Snwhitehorn}
494217309Snwhitehorn
495217309Snwhitehorn/*
496217309Snwhitehorn * Built-in bindings have a nonzero "win" member, and the associated binding
497217309Snwhitehorn * table can have more than one entry.  We keep those last, since lookups will
498217309Snwhitehorn * find the user-defined bindings first and use those.
499217309Snwhitehorn *
500217309Snwhitehorn * Sort "*" (all-widgets) entries past named widgets, since those are less
501217309Snwhitehorn * specific.
502217309Snwhitehorn */
503217309Snwhitehornstatic int
504217309Snwhitehorncompare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
505217309Snwhitehorn{
506217309Snwhitehorn    int result = 0;
507217309Snwhitehorn    if (a->win == b->win) {
508217309Snwhitehorn	if (!strcmp(a->name, b->name)) {
509217309Snwhitehorn	    result = a->binding[0].curses_key - b->binding[0].curses_key;
510251843Sbapt	} else if (!strcmp(b->name, WILDNAME)) {
511217309Snwhitehorn	    result = -1;
512251843Sbapt	} else if (!strcmp(a->name, WILDNAME)) {
513217309Snwhitehorn	    result = 1;
514217309Snwhitehorn	} else {
515217309Snwhitehorn	    result = dlg_strcmp(a->name, b->name);
516217309Snwhitehorn	}
517217309Snwhitehorn    } else if (b->win) {
518217309Snwhitehorn	result = -1;
519217309Snwhitehorn    } else {
520217309Snwhitehorn	result = 1;
521217309Snwhitehorn    }
522217309Snwhitehorn    return result;
523217309Snwhitehorn}
524217309Snwhitehorn
525217309Snwhitehorn/*
526217309Snwhitehorn * Find a user-defined binding, given the curses key code.  If it does not
527217309Snwhitehorn * exist, create a new one, inserting it into the linked list, keeping it
528217309Snwhitehorn * sorted to simplify lookups for user-defined bindings that can override
529217309Snwhitehorn * the built-in bindings.
530217309Snwhitehorn */
531217309Snwhitehornstatic DLG_KEYS_BINDING *
532217309Snwhitehornmake_binding(char *widget, int curses_key, int is_function, int dialog_key)
533217309Snwhitehorn{
534217309Snwhitehorn    LIST_BINDINGS *entry = 0;
535217309Snwhitehorn    DLG_KEYS_BINDING *data = 0;
536217309Snwhitehorn    char *name;
537217309Snwhitehorn    LIST_BINDINGS *p, *q;
538217309Snwhitehorn    DLG_KEYS_BINDING *result = find_binding(widget, curses_key);
539217309Snwhitehorn
540217309Snwhitehorn    if (result == 0
541217309Snwhitehorn	&& (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0
542217309Snwhitehorn	&& (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0
543217309Snwhitehorn	&& (name = dlg_strclone(widget)) != 0) {
544217309Snwhitehorn
545217309Snwhitehorn	entry->name = name;
546217309Snwhitehorn	entry->binding = data;
547217309Snwhitehorn
548217309Snwhitehorn	data[0].is_function_key = is_function;
549217309Snwhitehorn	data[0].curses_key = curses_key;
550217309Snwhitehorn	data[0].dialog_key = dialog_key;
551217309Snwhitehorn
552217309Snwhitehorn	data[1] = end_keys_binding;
553217309Snwhitehorn
554217309Snwhitehorn	for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
555217309Snwhitehorn	    if (compare_bindings(entry, p) < 0) {
556217309Snwhitehorn		break;
557217309Snwhitehorn	    }
558217309Snwhitehorn	}
559217309Snwhitehorn	if (q != 0) {
560217309Snwhitehorn	    q->link = entry;
561217309Snwhitehorn	} else {
562217309Snwhitehorn	    all_bindings = entry;
563217309Snwhitehorn	}
564217309Snwhitehorn	if (p != 0) {
565217309Snwhitehorn	    entry->link = p;
566217309Snwhitehorn	}
567217309Snwhitehorn	result = data;
568217309Snwhitehorn    } else if (entry != 0) {
569217309Snwhitehorn	free(entry);
570217309Snwhitehorn	if (data)
571217309Snwhitehorn	    free(data);
572217309Snwhitehorn    }
573217309Snwhitehorn
574217309Snwhitehorn    return result;
575217309Snwhitehorn}
576217309Snwhitehorn
577217309Snwhitehorn/*
578217309Snwhitehorn * Parse the parameters of the "bindkeys" configuration-file entry.  This
579217309Snwhitehorn * expects widget name which may be "*", followed by curses key definition and
580217309Snwhitehorn * then dialog key definition.
581217309Snwhitehorn *
582217309Snwhitehorn * The curses key "should" be one of the names (ignoring case) from
583217309Snwhitehorn * curses_names[], but may also be a single control character (prefix "^" or
584217309Snwhitehorn * "~" depending on whether it is C0 or C1), or an escaped single character.
585217309Snwhitehorn * Binding a printable character with dialog is possible but not useful.
586217309Snwhitehorn *
587217309Snwhitehorn * The dialog key must be one of the names from dialog_names[].
588217309Snwhitehorn */
589217309Snwhitehornint
590217309Snwhitehorndlg_parse_bindkey(char *params)
591217309Snwhitehorn{
592217309Snwhitehorn    char *p = skip_white(params);
593217309Snwhitehorn    char *q;
594217309Snwhitehorn    bool escaped = FALSE;
595217309Snwhitehorn    int modified = 0;
596217309Snwhitehorn    int result = FALSE;
597217309Snwhitehorn    unsigned xx;
598217309Snwhitehorn    char *widget;
599217309Snwhitehorn    int is_function = FALSE;
600217309Snwhitehorn    int curses_key;
601217309Snwhitehorn    int dialog_key;
602217309Snwhitehorn
603217309Snwhitehorn    curses_key = -1;
604217309Snwhitehorn    dialog_key = -1;
605217309Snwhitehorn    widget = p;
606217309Snwhitehorn
607217309Snwhitehorn    p = skip_black(p);
608217309Snwhitehorn    if (p != widget && *p != '\0') {
609217309Snwhitehorn	*p++ = '\0';
610251843Sbapt	p = skip_white(p);
611217309Snwhitehorn	q = p;
612217309Snwhitehorn	while (*p != '\0' && curses_key < 0) {
613217309Snwhitehorn	    if (escaped) {
614217309Snwhitehorn		escaped = FALSE;
615217309Snwhitehorn		curses_key = *p;
616217309Snwhitehorn	    } else if (*p == '\\') {
617217309Snwhitehorn		escaped = TRUE;
618217309Snwhitehorn	    } else if (modified) {
619217309Snwhitehorn		if (*p == '?') {
620217309Snwhitehorn		    curses_key = ((modified == '^')
621217309Snwhitehorn				  ? 127
622217309Snwhitehorn				  : 255);
623217309Snwhitehorn		} else {
624217309Snwhitehorn		    curses_key = ((modified == '^')
625217309Snwhitehorn				  ? (*p & 0x1f)
626217309Snwhitehorn				  : ((*p & 0x1f) | 0x80));
627217309Snwhitehorn		}
628217309Snwhitehorn	    } else if (*p == '^') {
629217309Snwhitehorn		modified = *p;
630217309Snwhitehorn	    } else if (*p == '~') {
631217309Snwhitehorn		modified = *p;
632217309Snwhitehorn	    } else if (isspace(UCH(*p))) {
633217309Snwhitehorn		break;
634217309Snwhitehorn	    }
635217309Snwhitehorn	    ++p;
636217309Snwhitehorn	}
637217309Snwhitehorn	if (!isspace(UCH(*p))) {
638217309Snwhitehorn	    ;
639217309Snwhitehorn	} else {
640217309Snwhitehorn	    *p++ = '\0';
641217309Snwhitehorn	    if (curses_key < 0) {
642217309Snwhitehorn		char fprefix[2];
643217309Snwhitehorn		char check[2];
644217309Snwhitehorn		int keynumber;
645217309Snwhitehorn		if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) {
646217309Snwhitehorn		    curses_key = KEY_F(keynumber);
647217309Snwhitehorn		    is_function = TRUE;
648217309Snwhitehorn		} else {
649217309Snwhitehorn		    for (xx = 0; xx < COUNT_CURSES; ++xx) {
650217309Snwhitehorn			if (!dlg_strcmp(curses_names[xx].name, q)) {
651217309Snwhitehorn			    curses_key = curses_names[xx].code;
652251843Sbapt			    is_function = (curses_key >= KEY_MIN);
653217309Snwhitehorn			    break;
654217309Snwhitehorn			}
655217309Snwhitehorn		    }
656217309Snwhitehorn		}
657217309Snwhitehorn	    }
658217309Snwhitehorn	}
659217309Snwhitehorn	q = skip_white(p);
660217309Snwhitehorn	p = skip_black(q);
661217309Snwhitehorn	if (p != q) {
662217309Snwhitehorn	    for (xx = 0; xx < COUNT_DIALOG; ++xx) {
663217309Snwhitehorn		if (!dlg_strcmp(dialog_names[xx].name, q)) {
664217309Snwhitehorn		    dialog_key = dialog_names[xx].code;
665217309Snwhitehorn		    break;
666217309Snwhitehorn		}
667217309Snwhitehorn	    }
668217309Snwhitehorn	}
669217309Snwhitehorn	if (*widget != '\0'
670217309Snwhitehorn	    && curses_key >= 0
671217309Snwhitehorn	    && dialog_key >= 0
672217309Snwhitehorn	    && make_binding(widget, curses_key, is_function, dialog_key) != 0) {
673217309Snwhitehorn	    result = TRUE;
674217309Snwhitehorn	}
675217309Snwhitehorn    }
676217309Snwhitehorn    return result;
677217309Snwhitehorn}
678217309Snwhitehorn
679217309Snwhitehornstatic void
680217309Snwhitehorndump_curses_key(FILE *fp, int curses_key)
681217309Snwhitehorn{
682217309Snwhitehorn    if (curses_key > KEY_MIN) {
683217309Snwhitehorn	unsigned n;
684217309Snwhitehorn	bool found = FALSE;
685217309Snwhitehorn	for (n = 0; n < COUNT_CURSES; ++n) {
686217309Snwhitehorn	    if (curses_names[n].code == curses_key) {
687217309Snwhitehorn		fprintf(fp, "%s", curses_names[n].name);
688217309Snwhitehorn		found = TRUE;
689217309Snwhitehorn		break;
690217309Snwhitehorn	    }
691217309Snwhitehorn	}
692217309Snwhitehorn	if (!found) {
693217309Snwhitehorn	    if (curses_key >= KEY_F(0)) {
694217309Snwhitehorn		fprintf(fp, "F%d", curses_key - KEY_F(0));
695217309Snwhitehorn	    } else {
696217309Snwhitehorn		fprintf(fp, "curses%d", curses_key);
697217309Snwhitehorn	    }
698217309Snwhitehorn	}
699217309Snwhitehorn    } else if (curses_key >= 0 && curses_key < 32) {
700217309Snwhitehorn	fprintf(fp, "^%c", curses_key + 64);
701217309Snwhitehorn    } else if (curses_key == 127) {
702217309Snwhitehorn	fprintf(fp, "^?");
703217309Snwhitehorn    } else if (curses_key >= 128 && curses_key < 160) {
704217309Snwhitehorn	fprintf(fp, "~%c", curses_key - 64);
705217309Snwhitehorn    } else if (curses_key == 255) {
706217309Snwhitehorn	fprintf(fp, "~?");
707217309Snwhitehorn    } else {
708217309Snwhitehorn	fprintf(fp, "\\%c", curses_key);
709217309Snwhitehorn    }
710217309Snwhitehorn}
711217309Snwhitehorn
712217309Snwhitehornstatic void
713217309Snwhitehorndump_dialog_key(FILE *fp, int dialog_key)
714217309Snwhitehorn{
715217309Snwhitehorn    unsigned n;
716217309Snwhitehorn    bool found = FALSE;
717217309Snwhitehorn    for (n = 0; n < COUNT_DIALOG; ++n) {
718217309Snwhitehorn	if (dialog_names[n].code == dialog_key) {
719217309Snwhitehorn	    fputs(dialog_names[n].name, fp);
720217309Snwhitehorn	    found = TRUE;
721217309Snwhitehorn	    break;
722217309Snwhitehorn	}
723217309Snwhitehorn    }
724217309Snwhitehorn    if (!found) {
725217309Snwhitehorn	fprintf(fp, "dialog%d", dialog_key);
726217309Snwhitehorn    }
727217309Snwhitehorn}
728217309Snwhitehorn
729217309Snwhitehornstatic void
730217309Snwhitehorndump_one_binding(FILE *fp, const char *widget, DLG_KEYS_BINDING * binding)
731217309Snwhitehorn{
732217309Snwhitehorn    fprintf(fp, "bindkey %s ", widget);
733217309Snwhitehorn    dump_curses_key(fp, binding->curses_key);
734217309Snwhitehorn    fputc(' ', fp);
735217309Snwhitehorn    dump_dialog_key(fp, binding->dialog_key);
736217309Snwhitehorn    fputc('\n', fp);
737217309Snwhitehorn}
738217309Snwhitehorn
739251843Sbapt/*
740251843Sbapt * Dump bindings for the given window.  If it is a null, then this dumps the
741251843Sbapt * initial bindings which were loaded from the rc-file that are used as
742251843Sbapt * overall defaults.
743251843Sbapt */
744217309Snwhitehornvoid
745251843Sbaptdlg_dump_window_keys(FILE *fp, WINDOW *win)
746217309Snwhitehorn{
747251843Sbapt    if (fp != 0) {
748251843Sbapt	LIST_BINDINGS *p;
749251843Sbapt	DLG_KEYS_BINDING *q;
750251843Sbapt	const char *last = "";
751217309Snwhitehorn
752251843Sbapt	for (p = all_bindings; p != 0; p = p->link) {
753251843Sbapt	    if (p->win == win) {
754217309Snwhitehorn		if (dlg_strcmp(last, p->name)) {
755217309Snwhitehorn		    fprintf(fp, "\n# key bindings for %s widgets\n",
756251843Sbapt			    !strcmp(p->name, WILDNAME) ? "all" : p->name);
757217309Snwhitehorn		    last = p->name;
758217309Snwhitehorn		}
759251843Sbapt		for (q = p->binding; q->is_function_key >= 0; ++q) {
760251843Sbapt		    dump_one_binding(fp, p->name, q);
761251843Sbapt		}
762217309Snwhitehorn	    }
763217309Snwhitehorn	}
764217309Snwhitehorn    }
765217309Snwhitehorn}
766251843Sbapt
767251843Sbapt/*
768251843Sbapt * Dump all of the bindings which are not specific to a given widget, i.e.,
769251843Sbapt * the "win" member is null.
770251843Sbapt */
771251843Sbaptvoid
772251843Sbaptdlg_dump_keys(FILE *fp)
773251843Sbapt{
774251843Sbapt    if (fp != 0) {
775251843Sbapt	LIST_BINDINGS *p;
776251843Sbapt	unsigned count = 0;
777251843Sbapt
778251843Sbapt	for (p = all_bindings; p != 0; p = p->link) {
779251843Sbapt	    if (p->win == 0) {
780251843Sbapt		++count;
781251843Sbapt	    }
782251843Sbapt	}
783251843Sbapt	if (count != 0) {
784251843Sbapt	    dlg_dump_window_keys(fp, 0);
785251843Sbapt	}
786251843Sbapt    }
787251843Sbapt}
788217309Snwhitehorn#endif /* HAVE_RC_FILE */
789