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