Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/dialog/dlg_keys.c
39475 views
1
/*
2
* $Id: dlg_keys.c,v 1.58 2020/11/26 17:11:56 Glenn.Herteg Exp $
3
*
4
* dlg_keys.c -- runtime binding support for dialog
5
*
6
* Copyright 2006-2019,2020 Thomas E. Dickey
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU Lesser General Public License, version 2.1
10
* as published by the Free Software Foundation.
11
*
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
16
*
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this program; if not, write to
19
* Free Software Foundation, Inc.
20
* 51 Franklin St., Fifth Floor
21
* Boston, MA 02110, USA.
22
*/
23
24
#include <dialog.h>
25
#include <dlg_keys.h>
26
#include <dlg_internals.h>
27
28
#define LIST_BINDINGS struct _list_bindings
29
30
#define CHR_BACKSLASH '\\'
31
#define IsOctal(ch) ((ch) >= '0' && (ch) <= '7')
32
33
LIST_BINDINGS {
34
LIST_BINDINGS *link;
35
WINDOW *win; /* window on which widget gets input */
36
const char *name; /* widget name */
37
bool buttons; /* true only for dlg_register_buttons() */
38
DLG_KEYS_BINDING *binding; /* list of bindings */
39
};
40
41
#define WILDNAME "*"
42
static LIST_BINDINGS *all_bindings;
43
static const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING;
44
45
/*
46
* For a given named widget's window, associate a binding table.
47
*/
48
void
49
dlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding)
50
{
51
LIST_BINDINGS *p, *q;
52
53
for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
54
if (p->win == win && !strcmp(p->name, name)) {
55
p->binding = binding;
56
return;
57
}
58
}
59
/* add built-in bindings at the end of the list (see compare_bindings). */
60
if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
61
p->win = win;
62
p->name = name;
63
p->binding = binding;
64
if (q != 0) {
65
q->link = p;
66
} else {
67
all_bindings = p;
68
}
69
}
70
#if defined(HAVE_DLG_TRACE) && defined(HAVE_RC_FILE)
71
/*
72
* Trace the binding information assigned to this window. For most widgets
73
* there is only one binding table. forms have two, so the trace will be
74
* longer. Since compiled-in bindings are only visible when the widget is
75
* registered, there is no other way to see what bindings are available,
76
* than by running dialog and tracing it.
77
*/
78
DLG_TRACE(("# dlg_register_window %s\n", name));
79
dlg_dump_keys(dialog_state.trace_output);
80
dlg_dump_window_keys(dialog_state.trace_output, win);
81
DLG_TRACE(("# ...done dlg_register_window %s\n", name));
82
#endif
83
}
84
85
/*
86
* Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file
87
* definitions, depending on whether 'win' is null.
88
*/
89
static int
90
key_is_bound(WINDOW *win, const char *name, int curses_key, int function_key)
91
{
92
LIST_BINDINGS *p;
93
94
for (p = all_bindings; p != 0; p = p->link) {
95
if (p->win == win && !dlg_strcmp(p->name, name)) {
96
int n;
97
for (n = 0; p->binding[n].is_function_key >= 0; ++n) {
98
if (p->binding[n].curses_key == curses_key
99
&& p->binding[n].is_function_key == function_key) {
100
return TRUE;
101
}
102
}
103
}
104
}
105
return FALSE;
106
}
107
108
/*
109
* Call this function after dlg_register_window(), for the list of button
110
* labels associated with the widget.
111
*
112
* Ensure that dlg_lookup_key() will not accidentally translate a key that
113
* we would like to use for a button abbreviation to some other key, e.g.,
114
* h/j/k/l for navigation into a cursor key. Do this by binding the key
115
* to itself.
116
*
117
* See dlg_char_to_button().
118
*/
119
void
120
dlg_register_buttons(WINDOW *win, const char *name, const char **buttons)
121
{
122
int n;
123
LIST_BINDINGS *p;
124
DLG_KEYS_BINDING *q;
125
126
if (buttons == 0)
127
return;
128
129
for (n = 0; buttons[n] != 0; ++n) {
130
int curses_key = dlg_button_to_char(buttons[n]);
131
132
/* ignore binding if there is no key to bind */
133
if (curses_key < 0)
134
continue;
135
136
/* ignore multibyte characters */
137
if (curses_key >= KEY_MIN)
138
continue;
139
140
/* if it is not bound in the widget, skip it (no conflicts) */
141
if (!key_is_bound(win, name, curses_key, FALSE))
142
continue;
143
144
#ifdef HAVE_RC_FILE
145
/* if it is bound in the rc-file, skip it */
146
if (key_is_bound(0, name, curses_key, FALSE))
147
continue;
148
#endif
149
150
if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) {
151
if ((q = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0) {
152
q[0].is_function_key = 0;
153
q[0].curses_key = curses_key;
154
q[0].dialog_key = curses_key;
155
q[1] = end_keys_binding;
156
157
p->win = win;
158
p->name = name;
159
p->buttons = TRUE;
160
p->binding = q;
161
162
/* put these at the beginning, to override the widget's table */
163
p->link = all_bindings;
164
all_bindings = p;
165
} else {
166
free(p);
167
}
168
}
169
}
170
}
171
172
/*
173
* Remove the bindings for a given window.
174
*/
175
void
176
dlg_unregister_window(WINDOW *win)
177
{
178
LIST_BINDINGS *p, *q;
179
180
for (p = all_bindings, q = 0; p != 0; p = p->link) {
181
if (p->win == win) {
182
if (q != 0) {
183
q->link = p->link;
184
} else {
185
all_bindings = p->link;
186
}
187
/* the user-defined and buttons-bindings all are length=1 */
188
if (p->binding[1].is_function_key < 0)
189
free(p->binding);
190
free(p);
191
dlg_unregister_window(win);
192
break;
193
}
194
q = p;
195
}
196
}
197
198
/*
199
* Call this after wgetch(), using the same window pointer and passing
200
* the curses-key.
201
*
202
* If there is no binding associated with the widget, it simply returns
203
* the given curses-key.
204
*
205
* Parameters:
206
* win is the window on which the wgetch() was done.
207
* curses_key is the value returned by wgetch().
208
* fkey in/out (on input, it is nonzero if curses_key is a function key,
209
* and on output, it is nonzero if the result is a function key).
210
*/
211
int
212
dlg_lookup_key(WINDOW *win, int curses_key, int *fkey)
213
{
214
LIST_BINDINGS *p;
215
DLG_KEYS_BINDING *q;
216
217
/*
218
* Ignore mouse clicks, since they are already encoded properly.
219
*/
220
#ifdef KEY_MOUSE
221
if (*fkey != 0 && curses_key == KEY_MOUSE) {
222
;
223
} else
224
#endif
225
/*
226
* Ignore resize events, since they are already encoded properly.
227
*/
228
#ifdef KEY_RESIZE
229
if (*fkey != 0 && curses_key == KEY_RESIZE) {
230
;
231
} else
232
#endif
233
if (*fkey == 0 || curses_key < KEY_MAX) {
234
const char *name = WILDNAME;
235
if (win != 0) {
236
for (p = all_bindings; p != 0; p = p->link) {
237
if (p->win == win) {
238
name = p->name;
239
break;
240
}
241
}
242
}
243
for (p = all_bindings; p != 0; p = p->link) {
244
if (p->win == win ||
245
(p->win == 0 &&
246
(!strcmp(p->name, name) || !strcmp(p->name, WILDNAME)))) {
247
int function_key = (*fkey != 0);
248
for (q = p->binding; q->is_function_key >= 0; ++q) {
249
if (p->buttons
250
&& !function_key
251
&& q->curses_key == (int) dlg_toupper(curses_key)) {
252
*fkey = 0;
253
return q->dialog_key;
254
}
255
if (q->curses_key == curses_key
256
&& q->is_function_key == function_key) {
257
*fkey = q->dialog_key;
258
return *fkey;
259
}
260
}
261
}
262
}
263
}
264
return curses_key;
265
}
266
267
/*
268
* Test a dialog internal keycode to see if it corresponds to one of the push
269
* buttons on the widget such as "OK".
270
*
271
* This is only useful if there are user-defined key bindings, since there are
272
* no built-in bindings that map directly to DLGK_OK, etc.
273
*
274
* See also dlg_ok_buttoncode().
275
*/
276
int
277
dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp)
278
{
279
int done = FALSE;
280
281
DLG_TRACE(("# dlg_result_key(dialog_key=%d, fkey=%d)\n", dialog_key, fkey));
282
#ifdef KEY_RESIZE
283
if (dialog_state.had_resize) {
284
if (dialog_key == ERR) {
285
dialog_key = 0;
286
} else {
287
dialog_state.had_resize = FALSE;
288
}
289
} else if (fkey && dialog_key == KEY_RESIZE) {
290
dialog_state.had_resize = TRUE;
291
}
292
#endif
293
#ifdef HAVE_RC_FILE
294
if (fkey) {
295
switch ((DLG_KEYS_ENUM) dialog_key) {
296
case DLGK_OK:
297
if (!dialog_vars.nook) {
298
*resultp = DLG_EXIT_OK;
299
done = TRUE;
300
}
301
break;
302
case DLGK_CANCEL:
303
if (!dialog_vars.nocancel) {
304
*resultp = DLG_EXIT_CANCEL;
305
done = TRUE;
306
}
307
break;
308
case DLGK_EXTRA:
309
if (dialog_vars.extra_button) {
310
*resultp = DLG_EXIT_EXTRA;
311
done = TRUE;
312
}
313
break;
314
case DLGK_HELP:
315
if (dialog_vars.help_button) {
316
*resultp = DLG_EXIT_HELP;
317
done = TRUE;
318
}
319
break;
320
case DLGK_ESC:
321
*resultp = DLG_EXIT_ESC;
322
done = TRUE;
323
break;
324
default:
325
break;
326
}
327
} else
328
#endif
329
if (dialog_key == ESC) {
330
*resultp = DLG_EXIT_ESC;
331
done = TRUE;
332
} else if (dialog_key == ERR) {
333
*resultp = DLG_EXIT_ERROR;
334
done = TRUE;
335
}
336
337
return done;
338
}
339
340
/*
341
* If a key was bound to one of the button-codes in dlg_result_key(), fake
342
* a button-value and an "Enter" key to cause the calling widget to return
343
* the corresponding status.
344
*
345
* See dlg_ok_buttoncode(), which maps settings for ok/extra/help and button
346
* number into exit-code.
347
*/
348
int
349
dlg_button_key(int exit_code, int *button, int *dialog_key, int *fkey)
350
{
351
int changed = FALSE;
352
switch (exit_code) {
353
case DLG_EXIT_OK:
354
if (!dialog_vars.nook) {
355
*button = 0;
356
changed = TRUE;
357
}
358
break;
359
case DLG_EXIT_EXTRA:
360
if (dialog_vars.extra_button) {
361
*button = dialog_vars.nook ? 0 : 1;
362
changed = TRUE;
363
}
364
break;
365
case DLG_EXIT_CANCEL:
366
if (!dialog_vars.nocancel) {
367
*button = dialog_vars.nook ? 1 : 2;
368
changed = TRUE;
369
}
370
break;
371
case DLG_EXIT_HELP:
372
if (dialog_vars.help_button) {
373
int cancel = dialog_vars.nocancel ? 0 : 1;
374
int extra = dialog_vars.extra_button ? 1 : 0;
375
int okay = dialog_vars.nook ? 0 : 1;
376
*button = okay + extra + cancel;
377
changed = TRUE;
378
}
379
break;
380
}
381
if (changed) {
382
DLG_TRACE(("# dlg_button_key(%d:%s) button %d\n",
383
exit_code, dlg_exitcode2s(exit_code), *button));
384
*dialog_key = *fkey = DLGK_ENTER;
385
}
386
return changed;
387
}
388
389
int
390
dlg_ok_button_key(int exit_code, int *button, int *dialog_key, int *fkey)
391
{
392
int result;
393
DIALOG_VARS save;
394
395
dlg_save_vars(&save);
396
dialog_vars.nocancel = TRUE;
397
398
result = dlg_button_key(exit_code, button, dialog_key, fkey);
399
400
dlg_restore_vars(&save);
401
return result;
402
}
403
404
#ifdef HAVE_RC_FILE
405
typedef struct {
406
const char *name;
407
int code;
408
} CODENAME;
409
410
#define ASCII_NAME(name,code) { #name, code }
411
#define CURSES_NAME(upper) { #upper, KEY_ ## upper }
412
#define COUNT_CURSES TableSize(curses_names)
413
static const CODENAME curses_names[] =
414
{
415
ASCII_NAME(ESC, '\033'),
416
ASCII_NAME(CR, '\r'),
417
ASCII_NAME(LF, '\n'),
418
ASCII_NAME(FF, '\f'),
419
ASCII_NAME(TAB, '\t'),
420
ASCII_NAME(DEL, '\177'),
421
422
CURSES_NAME(DOWN),
423
CURSES_NAME(UP),
424
CURSES_NAME(LEFT),
425
CURSES_NAME(RIGHT),
426
CURSES_NAME(HOME),
427
CURSES_NAME(BACKSPACE),
428
CURSES_NAME(F0),
429
CURSES_NAME(DL),
430
CURSES_NAME(IL),
431
CURSES_NAME(DC),
432
CURSES_NAME(IC),
433
CURSES_NAME(EIC),
434
CURSES_NAME(CLEAR),
435
CURSES_NAME(EOS),
436
CURSES_NAME(EOL),
437
CURSES_NAME(SF),
438
CURSES_NAME(SR),
439
CURSES_NAME(NPAGE),
440
CURSES_NAME(PPAGE),
441
CURSES_NAME(STAB),
442
CURSES_NAME(CTAB),
443
CURSES_NAME(CATAB),
444
CURSES_NAME(ENTER),
445
CURSES_NAME(PRINT),
446
CURSES_NAME(LL),
447
CURSES_NAME(A1),
448
CURSES_NAME(A3),
449
CURSES_NAME(B2),
450
CURSES_NAME(C1),
451
CURSES_NAME(C3),
452
CURSES_NAME(BTAB),
453
CURSES_NAME(BEG),
454
CURSES_NAME(CANCEL),
455
CURSES_NAME(CLOSE),
456
CURSES_NAME(COMMAND),
457
CURSES_NAME(COPY),
458
CURSES_NAME(CREATE),
459
CURSES_NAME(END),
460
CURSES_NAME(EXIT),
461
CURSES_NAME(FIND),
462
CURSES_NAME(HELP),
463
CURSES_NAME(MARK),
464
CURSES_NAME(MESSAGE),
465
CURSES_NAME(MOVE),
466
CURSES_NAME(NEXT),
467
CURSES_NAME(OPEN),
468
CURSES_NAME(OPTIONS),
469
CURSES_NAME(PREVIOUS),
470
CURSES_NAME(REDO),
471
CURSES_NAME(REFERENCE),
472
CURSES_NAME(REFRESH),
473
CURSES_NAME(REPLACE),
474
CURSES_NAME(RESTART),
475
CURSES_NAME(RESUME),
476
CURSES_NAME(SAVE),
477
CURSES_NAME(SBEG),
478
CURSES_NAME(SCANCEL),
479
CURSES_NAME(SCOMMAND),
480
CURSES_NAME(SCOPY),
481
CURSES_NAME(SCREATE),
482
CURSES_NAME(SDC),
483
CURSES_NAME(SDL),
484
CURSES_NAME(SELECT),
485
CURSES_NAME(SEND),
486
CURSES_NAME(SEOL),
487
CURSES_NAME(SEXIT),
488
CURSES_NAME(SFIND),
489
CURSES_NAME(SHELP),
490
CURSES_NAME(SHOME),
491
CURSES_NAME(SIC),
492
CURSES_NAME(SLEFT),
493
CURSES_NAME(SMESSAGE),
494
CURSES_NAME(SMOVE),
495
CURSES_NAME(SNEXT),
496
CURSES_NAME(SOPTIONS),
497
CURSES_NAME(SPREVIOUS),
498
CURSES_NAME(SPRINT),
499
CURSES_NAME(SREDO),
500
CURSES_NAME(SREPLACE),
501
CURSES_NAME(SRIGHT),
502
CURSES_NAME(SRSUME),
503
CURSES_NAME(SSAVE),
504
CURSES_NAME(SSUSPEND),
505
CURSES_NAME(SUNDO),
506
CURSES_NAME(SUSPEND),
507
CURSES_NAME(UNDO),
508
};
509
510
#define DIALOG_NAME(upper) { #upper, DLGK_ ## upper }
511
#define COUNT_DIALOG TableSize(dialog_names)
512
static const CODENAME dialog_names[] =
513
{
514
DIALOG_NAME(OK),
515
DIALOG_NAME(CANCEL),
516
DIALOG_NAME(EXTRA),
517
DIALOG_NAME(HELP),
518
DIALOG_NAME(ESC),
519
DIALOG_NAME(PAGE_FIRST),
520
DIALOG_NAME(PAGE_LAST),
521
DIALOG_NAME(PAGE_NEXT),
522
DIALOG_NAME(PAGE_PREV),
523
DIALOG_NAME(ITEM_FIRST),
524
DIALOG_NAME(ITEM_LAST),
525
DIALOG_NAME(ITEM_NEXT),
526
DIALOG_NAME(ITEM_PREV),
527
DIALOG_NAME(FIELD_FIRST),
528
DIALOG_NAME(FIELD_LAST),
529
DIALOG_NAME(FIELD_NEXT),
530
DIALOG_NAME(FIELD_PREV),
531
DIALOG_NAME(FORM_FIRST),
532
DIALOG_NAME(FORM_LAST),
533
DIALOG_NAME(FORM_NEXT),
534
DIALOG_NAME(FORM_PREV),
535
DIALOG_NAME(GRID_UP),
536
DIALOG_NAME(GRID_DOWN),
537
DIALOG_NAME(GRID_LEFT),
538
DIALOG_NAME(GRID_RIGHT),
539
DIALOG_NAME(DELETE_LEFT),
540
DIALOG_NAME(DELETE_RIGHT),
541
DIALOG_NAME(DELETE_ALL),
542
DIALOG_NAME(ENTER),
543
DIALOG_NAME(BEGIN),
544
DIALOG_NAME(FINAL),
545
DIALOG_NAME(SELECT),
546
DIALOG_NAME(HELPFILE),
547
DIALOG_NAME(TRACE),
548
DIALOG_NAME(TOGGLE),
549
DIALOG_NAME(LEAVE)
550
};
551
552
#define MAP2(letter,actual) { letter, actual }
553
554
static const struct {
555
int letter;
556
int actual;
557
} escaped_letters[] = {
558
559
MAP2('a', DLG_CTRL('G')),
560
MAP2('b', DLG_CTRL('H')),
561
MAP2('f', DLG_CTRL('L')),
562
MAP2('n', DLG_CTRL('J')),
563
MAP2('r', DLG_CTRL('M')),
564
MAP2('s', CHR_SPACE),
565
MAP2('t', DLG_CTRL('I')),
566
MAP2('\\', '\\'),
567
};
568
569
#undef MAP2
570
571
static char *
572
skip_white(char *s)
573
{
574
while (*s != '\0' && isspace(UCH(*s)))
575
++s;
576
return s;
577
}
578
579
static char *
580
skip_black(char *s)
581
{
582
while (*s != '\0' && !isspace(UCH(*s)))
583
++s;
584
return s;
585
}
586
587
/*
588
* Find a user-defined binding, given the curses key code.
589
*/
590
static DLG_KEYS_BINDING *
591
find_binding(char *widget, int curses_key)
592
{
593
LIST_BINDINGS *p;
594
DLG_KEYS_BINDING *result = 0;
595
596
for (p = all_bindings; p != 0; p = p->link) {
597
if (p->win == 0
598
&& !dlg_strcmp(p->name, widget)
599
&& p->binding->curses_key == curses_key) {
600
result = p->binding;
601
break;
602
}
603
}
604
return result;
605
}
606
607
/*
608
* Built-in bindings have a nonzero "win" member, and the associated binding
609
* table can have more than one entry. We keep those last, since lookups will
610
* find the user-defined bindings first and use those.
611
*
612
* Sort "*" (all-widgets) entries past named widgets, since those are less
613
* specific.
614
*/
615
static int
616
compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b)
617
{
618
int result = 0;
619
if (a->win == b->win) {
620
if (!strcmp(a->name, b->name)) {
621
result = a->binding[0].curses_key - b->binding[0].curses_key;
622
} else if (!strcmp(b->name, WILDNAME)) {
623
result = -1;
624
} else if (!strcmp(a->name, WILDNAME)) {
625
result = 1;
626
} else {
627
result = dlg_strcmp(a->name, b->name);
628
}
629
} else if (b->win) {
630
result = -1;
631
} else {
632
result = 1;
633
}
634
return result;
635
}
636
637
/*
638
* Find a user-defined binding, given the curses key code. If it does not
639
* exist, create a new one, inserting it into the linked list, keeping it
640
* sorted to simplify lookups for user-defined bindings that can override
641
* the built-in bindings.
642
*/
643
static DLG_KEYS_BINDING *
644
make_binding(char *widget, int curses_key, int is_function, int dialog_key)
645
{
646
LIST_BINDINGS *entry = 0;
647
DLG_KEYS_BINDING *data = 0;
648
char *name;
649
DLG_KEYS_BINDING *result = find_binding(widget, curses_key);
650
651
if (result == 0
652
&& (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0
653
&& (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0
654
&& (name = dlg_strclone(widget)) != 0) {
655
LIST_BINDINGS *p, *q;
656
657
entry->name = name;
658
entry->binding = data;
659
660
data[0].is_function_key = is_function;
661
data[0].curses_key = curses_key;
662
data[0].dialog_key = dialog_key;
663
664
data[1] = end_keys_binding;
665
666
for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) {
667
if (compare_bindings(entry, p) < 0) {
668
break;
669
}
670
}
671
if (q != 0) {
672
q->link = entry;
673
} else {
674
all_bindings = entry;
675
}
676
if (p != 0) {
677
entry->link = p;
678
}
679
result = data;
680
} else if (entry != 0) {
681
free(entry);
682
if (data)
683
free(data);
684
}
685
686
return result;
687
}
688
689
static int
690
decode_escaped(char **string)
691
{
692
int result = 0;
693
694
if (IsOctal(**string)) {
695
int limit = 3;
696
while (limit-- > 0 && IsOctal(**string)) {
697
int ch = (**string);
698
*string += 1;
699
result = (result << 3) | (ch - '0');
700
}
701
} else {
702
unsigned n;
703
704
for (n = 0; n < TableSize(escaped_letters); ++n) {
705
if (**string == escaped_letters[n].letter) {
706
*string += 1;
707
result = escaped_letters[n].actual;
708
break;
709
}
710
}
711
}
712
return result;
713
}
714
715
static char *
716
encode_escaped(int value)
717
{
718
static char result[80];
719
unsigned n;
720
bool found = FALSE;
721
for (n = 0; n < TableSize(escaped_letters); ++n) {
722
if (value == escaped_letters[n].actual) {
723
found = TRUE;
724
sprintf(result, "%c", escaped_letters[n].letter);
725
break;
726
}
727
}
728
if (!found) {
729
sprintf(result, "%03o", value & 0xff);
730
}
731
return result;
732
}
733
734
/*
735
* Parse the parameters of the "bindkey" configuration-file entry. This
736
* expects widget name which may be "*", followed by curses key definition and
737
* then dialog key definition.
738
*
739
* The curses key "should" be one of the names (ignoring case) from
740
* curses_names[], but may also be a single control character (prefix "^" or
741
* "~" depending on whether it is C0 or C1), or an escaped single character.
742
* Binding a printable character with dialog is possible but not useful.
743
*
744
* The dialog key must be one of the names from dialog_names[].
745
*/
746
int
747
dlg_parse_bindkey(char *params)
748
{
749
char *p = skip_white(params);
750
int result = FALSE;
751
char *widget;
752
int curses_key;
753
int dialog_key;
754
755
curses_key = -1;
756
dialog_key = -1;
757
widget = p;
758
759
p = skip_black(p);
760
if (p != widget && *p != '\0') {
761
char *q;
762
unsigned xx;
763
bool escaped = FALSE;
764
int modified = 0;
765
int is_function = FALSE;
766
767
*p++ = '\0';
768
p = skip_white(p);
769
q = p;
770
while (*p != '\0' && curses_key < 0) {
771
if (escaped) {
772
escaped = FALSE;
773
curses_key = decode_escaped(&p);
774
} else if (*p == CHR_BACKSLASH) {
775
escaped = TRUE;
776
} else if (modified) {
777
if (*p == '?') {
778
curses_key = ((modified == '^')
779
? 127
780
: 255);
781
} else {
782
curses_key = ((modified == '^')
783
? (*p & 0x1f)
784
: ((*p & 0x1f) | 0x80));
785
}
786
} else if (*p == '^') {
787
modified = *p;
788
} else if (*p == '~') {
789
modified = *p;
790
} else if (isspace(UCH(*p))) {
791
break;
792
}
793
++p;
794
}
795
if (!isspace(UCH(*p))) {
796
;
797
} else {
798
*p++ = '\0';
799
if (curses_key < 0) {
800
char fprefix[2];
801
char check[2];
802
int keynumber;
803
if (sscanf(q, "%1[Ff]%d%c", fprefix, &keynumber, check) == 2) {
804
curses_key = KEY_F(keynumber);
805
is_function = TRUE;
806
} else {
807
for (xx = 0; xx < COUNT_CURSES; ++xx) {
808
if (!dlg_strcmp(curses_names[xx].name, q)) {
809
curses_key = curses_names[xx].code;
810
is_function = (curses_key >= KEY_MIN);
811
break;
812
}
813
}
814
}
815
}
816
}
817
q = skip_white(p);
818
p = skip_black(q);
819
if (p != q) {
820
for (xx = 0; xx < COUNT_DIALOG; ++xx) {
821
if (!dlg_strcmp(dialog_names[xx].name, q)) {
822
dialog_key = dialog_names[xx].code;
823
break;
824
}
825
}
826
}
827
if (*widget != '\0'
828
&& curses_key >= 0
829
&& dialog_key >= 0
830
&& make_binding(widget, curses_key, is_function, dialog_key) != 0) {
831
result = TRUE;
832
}
833
}
834
return result;
835
}
836
837
static void
838
dump_curses_key(FILE *fp, int curses_key)
839
{
840
if (curses_key > KEY_MIN) {
841
unsigned n;
842
bool found = FALSE;
843
for (n = 0; n < COUNT_CURSES; ++n) {
844
if (curses_names[n].code == curses_key) {
845
fprintf(fp, "%s", curses_names[n].name);
846
found = TRUE;
847
break;
848
}
849
}
850
if (!found) {
851
#ifdef KEY_MOUSE
852
if (is_DLGK_MOUSE(curses_key)) {
853
fprintf(fp, "MOUSE-");
854
dump_curses_key(fp, curses_key - M_EVENT);
855
} else
856
#endif
857
if (curses_key >= KEY_F(0)) {
858
fprintf(fp, "F%d", curses_key - KEY_F(0));
859
} else {
860
fprintf(fp, "curses%d", curses_key);
861
}
862
}
863
} else if (curses_key >= 0 && curses_key < 32) {
864
fprintf(fp, "^%c", curses_key + 64);
865
} else if (curses_key == 127) {
866
fprintf(fp, "^?");
867
} else if (curses_key >= 128 && curses_key < 160) {
868
fprintf(fp, "~%c", curses_key - 64);
869
} else if (curses_key == 255) {
870
fprintf(fp, "~?");
871
} else if (curses_key > 32 &&
872
curses_key < 127 &&
873
curses_key != CHR_BACKSLASH) {
874
fprintf(fp, "%c", curses_key);
875
} else {
876
fprintf(fp, "%c%s", CHR_BACKSLASH, encode_escaped(curses_key));
877
}
878
}
879
880
static void
881
dump_dialog_key(FILE *fp, int dialog_key)
882
{
883
unsigned n;
884
bool found = FALSE;
885
for (n = 0; n < COUNT_DIALOG; ++n) {
886
if (dialog_names[n].code == dialog_key) {
887
fputs(dialog_names[n].name, fp);
888
found = TRUE;
889
break;
890
}
891
}
892
if (!found) {
893
fprintf(fp, "dialog%d", dialog_key);
894
}
895
}
896
897
static void
898
dump_one_binding(FILE *fp,
899
WINDOW *win,
900
const char *widget,
901
DLG_KEYS_BINDING * binding)
902
{
903
int actual;
904
int fkey = (binding->curses_key > 255);
905
906
fprintf(fp, "bindkey %s ", widget);
907
dump_curses_key(fp, binding->curses_key);
908
fputc(' ', fp);
909
dump_dialog_key(fp, binding->dialog_key);
910
actual = dlg_lookup_key(win, binding->curses_key, &fkey);
911
#ifdef KEY_MOUSE
912
if (is_DLGK_MOUSE(binding->curses_key) && is_DLGK_MOUSE(actual)) {
913
; /* EMPTY */
914
} else
915
#endif
916
if (actual != binding->dialog_key) {
917
fprintf(fp, "\t# overridden by ");
918
dump_dialog_key(fp, actual);
919
}
920
fputc('\n', fp);
921
}
922
923
/*
924
* Dump bindings for the given window. If it is a null, then this dumps the
925
* initial bindings which were loaded from the rc-file that are used as
926
* overall defaults.
927
*/
928
void
929
dlg_dump_window_keys(FILE *fp, WINDOW *win)
930
{
931
if (fp != 0) {
932
LIST_BINDINGS *p;
933
DLG_KEYS_BINDING *q;
934
const char *last = "";
935
936
for (p = all_bindings; p != 0; p = p->link) {
937
if (p->win == win) {
938
if (dlg_strcmp(last, p->name)) {
939
fprintf(fp, "# key bindings for %s widgets%s\n",
940
!strcmp(p->name, WILDNAME) ? "all" : p->name,
941
win == 0 ? " (user-defined)" : "");
942
last = p->name;
943
}
944
for (q = p->binding; q->is_function_key >= 0; ++q) {
945
dump_one_binding(fp, win, p->name, q);
946
}
947
}
948
}
949
}
950
}
951
952
/*
953
* Dump all of the bindings which are not specific to a given widget, i.e.,
954
* the "win" member is null.
955
*/
956
void
957
dlg_dump_keys(FILE *fp)
958
{
959
if (fp != 0) {
960
LIST_BINDINGS *p;
961
unsigned count = 0;
962
963
for (p = all_bindings; p != 0; p = p->link) {
964
if (p->win == 0) {
965
++count;
966
}
967
}
968
if (count != 0) {
969
dlg_dump_window_keys(fp, 0);
970
}
971
}
972
}
973
#endif /* HAVE_RC_FILE */
974
975