1 /* 2 * $Id: dlg_keys.c,v 1.45 2018/05/28 17:27:10 tom Exp $ 3 * 4 * dlg_keys.c -- runtime binding support for dialog 5 * 6 * Copyright 2006-2017,2018 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 27 #define LIST_BINDINGS struct _list_bindings 28 29 #define CHR_BACKSLASH '\\' 30 #define IsOctal(ch) ((ch) >= '0' && (ch) <= '7') 31 #define TableSize(name) (sizeof(name)/sizeof(name[0])) 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 #ifdef HAVE_RC_FILE 278 if (fkey) { 279 switch ((DLG_KEYS_ENUM) dialog_key) { 280 case DLGK_OK: 281 *resultp = DLG_EXIT_OK; 282 done = TRUE; 283 break; 284 case DLGK_CANCEL: 285 if (!dialog_vars.nocancel) { 286 *resultp = DLG_EXIT_CANCEL; 287 done = TRUE; 288 } 289 break; 290 case DLGK_EXTRA: 291 if (dialog_vars.extra_button) { 292 *resultp = DLG_EXIT_EXTRA; 293 done = TRUE; 294 } 295 break; 296 case DLGK_HELP: 297 if (dialog_vars.help_button) { 298 *resultp = DLG_EXIT_HELP; 299 done = TRUE; 300 } 301 break; 302 case DLGK_ESC: 303 *resultp = DLG_EXIT_ESC; 304 done = TRUE; 305 break; 306 default: 307 break; 308 } 309 } else 310 #endif 311 if (dialog_key == ESC) { 312 *resultp = DLG_EXIT_ESC; 313 done = TRUE; 314 } else if (dialog_key == ERR) { 315 *resultp = DLG_EXIT_ERROR; 316 done = TRUE; 317 } 318 319 return done; 320 } 321 322 #ifdef HAVE_RC_FILE 323 typedef struct { 324 const char *name; 325 int code; 326 } CODENAME; 327 328 #define ASCII_NAME(name,code) { #name, code } 329 #define CURSES_NAME(upper) { #upper, KEY_ ## upper } 330 #define COUNT_CURSES TableSize(curses_names) 331 static const CODENAME curses_names[] = 332 { 333 ASCII_NAME(ESC, '\033'), 334 ASCII_NAME(CR, '\r'), 335 ASCII_NAME(LF, '\n'), 336 ASCII_NAME(FF, '\f'), 337 ASCII_NAME(TAB, '\t'), 338 ASCII_NAME(DEL, '\177'), 339 340 CURSES_NAME(DOWN), 341 CURSES_NAME(UP), 342 CURSES_NAME(LEFT), 343 CURSES_NAME(RIGHT), 344 CURSES_NAME(HOME), 345 CURSES_NAME(BACKSPACE), 346 CURSES_NAME(F0), 347 CURSES_NAME(DL), 348 CURSES_NAME(IL), 349 CURSES_NAME(DC), 350 CURSES_NAME(IC), 351 CURSES_NAME(EIC), 352 CURSES_NAME(CLEAR), 353 CURSES_NAME(EOS), 354 CURSES_NAME(EOL), 355 CURSES_NAME(SF), 356 CURSES_NAME(SR), 357 CURSES_NAME(NPAGE), 358 CURSES_NAME(PPAGE), 359 CURSES_NAME(STAB), 360 CURSES_NAME(CTAB), 361 CURSES_NAME(CATAB), 362 CURSES_NAME(ENTER), 363 CURSES_NAME(PRINT), 364 CURSES_NAME(LL), 365 CURSES_NAME(A1), 366 CURSES_NAME(A3), 367 CURSES_NAME(B2), 368 CURSES_NAME(C1), 369 CURSES_NAME(C3), 370 CURSES_NAME(BTAB), 371 CURSES_NAME(BEG), 372 CURSES_NAME(CANCEL), 373 CURSES_NAME(CLOSE), 374 CURSES_NAME(COMMAND), 375 CURSES_NAME(COPY), 376 CURSES_NAME(CREATE), 377 CURSES_NAME(END), 378 CURSES_NAME(EXIT), 379 CURSES_NAME(FIND), 380 CURSES_NAME(HELP), 381 CURSES_NAME(MARK), 382 CURSES_NAME(MESSAGE), 383 CURSES_NAME(MOVE), 384 CURSES_NAME(NEXT), 385 CURSES_NAME(OPEN), 386 CURSES_NAME(OPTIONS), 387 CURSES_NAME(PREVIOUS), 388 CURSES_NAME(REDO), 389 CURSES_NAME(REFERENCE), 390 CURSES_NAME(REFRESH), 391 CURSES_NAME(REPLACE), 392 CURSES_NAME(RESTART), 393 CURSES_NAME(RESUME), 394 CURSES_NAME(SAVE), 395 CURSES_NAME(SBEG), 396 CURSES_NAME(SCANCEL), 397 CURSES_NAME(SCOMMAND), 398 CURSES_NAME(SCOPY), 399 CURSES_NAME(SCREATE), 400 CURSES_NAME(SDC), 401 CURSES_NAME(SDL), 402 CURSES_NAME(SELECT), 403 CURSES_NAME(SEND), 404 CURSES_NAME(SEOL), 405 CURSES_NAME(SEXIT), 406 CURSES_NAME(SFIND), 407 CURSES_NAME(SHELP), 408 CURSES_NAME(SHOME), 409 CURSES_NAME(SIC), 410 CURSES_NAME(SLEFT), 411 CURSES_NAME(SMESSAGE), 412 CURSES_NAME(SMOVE), 413 CURSES_NAME(SNEXT), 414 CURSES_NAME(SOPTIONS), 415 CURSES_NAME(SPREVIOUS), 416 CURSES_NAME(SPRINT), 417 CURSES_NAME(SREDO), 418 CURSES_NAME(SREPLACE), 419 CURSES_NAME(SRIGHT), 420 CURSES_NAME(SRSUME), 421 CURSES_NAME(SSAVE), 422 CURSES_NAME(SSUSPEND), 423 CURSES_NAME(SUNDO), 424 CURSES_NAME(SUSPEND), 425 CURSES_NAME(UNDO), 426 }; 427 428 #define DIALOG_NAME(upper) { #upper, DLGK_ ## upper } 429 #define COUNT_DIALOG TableSize(dialog_names) 430 static const CODENAME dialog_names[] = 431 { 432 DIALOG_NAME(OK), 433 DIALOG_NAME(CANCEL), 434 DIALOG_NAME(EXTRA), 435 DIALOG_NAME(HELP), 436 DIALOG_NAME(ESC), 437 DIALOG_NAME(PAGE_FIRST), 438 DIALOG_NAME(PAGE_LAST), 439 DIALOG_NAME(PAGE_NEXT), 440 DIALOG_NAME(PAGE_PREV), 441 DIALOG_NAME(ITEM_FIRST), 442 DIALOG_NAME(ITEM_LAST), 443 DIALOG_NAME(ITEM_NEXT), 444 DIALOG_NAME(ITEM_PREV), 445 DIALOG_NAME(FIELD_FIRST), 446 DIALOG_NAME(FIELD_LAST), 447 DIALOG_NAME(FIELD_NEXT), 448 DIALOG_NAME(FIELD_PREV), 449 DIALOG_NAME(FORM_FIRST), 450 DIALOG_NAME(FORM_LAST), 451 DIALOG_NAME(FORM_NEXT), 452 DIALOG_NAME(FORM_PREV), 453 DIALOG_NAME(GRID_UP), 454 DIALOG_NAME(GRID_DOWN), 455 DIALOG_NAME(GRID_LEFT), 456 DIALOG_NAME(GRID_RIGHT), 457 DIALOG_NAME(DELETE_LEFT), 458 DIALOG_NAME(DELETE_RIGHT), 459 DIALOG_NAME(DELETE_ALL), 460 DIALOG_NAME(ENTER), 461 DIALOG_NAME(BEGIN), 462 DIALOG_NAME(FINAL), 463 DIALOG_NAME(SELECT), 464 DIALOG_NAME(HELPFILE), 465 DIALOG_NAME(TRACE), 466 DIALOG_NAME(TOGGLE) 467 }; 468 469 #define MAP2(letter,actual) { letter, actual } 470 471 static const struct { 472 int letter; 473 int actual; 474 } escaped_letters[] = { 475 476 MAP2('a', DLG_CTRL('G')), 477 MAP2('b', DLG_CTRL('H')), 478 MAP2('f', DLG_CTRL('L')), 479 MAP2('n', DLG_CTRL('J')), 480 MAP2('r', DLG_CTRL('M')), 481 MAP2('s', CHR_SPACE), 482 MAP2('t', DLG_CTRL('I')), 483 MAP2('\\', '\\'), 484 }; 485 486 #undef MAP2 487 488 static char * 489 skip_white(char *s) 490 { 491 while (*s != '\0' && isspace(UCH(*s))) 492 ++s; 493 return s; 494 } 495 496 static char * 497 skip_black(char *s) 498 { 499 while (*s != '\0' && !isspace(UCH(*s))) 500 ++s; 501 return s; 502 } 503 504 /* 505 * Find a user-defined binding, given the curses key code. 506 */ 507 static DLG_KEYS_BINDING * 508 find_binding(char *widget, int curses_key) 509 { 510 LIST_BINDINGS *p; 511 DLG_KEYS_BINDING *result = 0; 512 513 for (p = all_bindings; p != 0; p = p->link) { 514 if (p->win == 0 515 && !dlg_strcmp(p->name, widget) 516 && p->binding->curses_key == curses_key) { 517 result = p->binding; 518 break; 519 } 520 } 521 return result; 522 } 523 524 /* 525 * Built-in bindings have a nonzero "win" member, and the associated binding 526 * table can have more than one entry. We keep those last, since lookups will 527 * find the user-defined bindings first and use those. 528 * 529 * Sort "*" (all-widgets) entries past named widgets, since those are less 530 * specific. 531 */ 532 static int 533 compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b) 534 { 535 int result = 0; 536 if (a->win == b->win) { 537 if (!strcmp(a->name, b->name)) { 538 result = a->binding[0].curses_key - b->binding[0].curses_key; 539 } else if (!strcmp(b->name, WILDNAME)) { 540 result = -1; 541 } else if (!strcmp(a->name, WILDNAME)) { 542 result = 1; 543 } else { 544 result = dlg_strcmp(a->name, b->name); 545 } 546 } else if (b->win) { 547 result = -1; 548 } else { 549 result = 1; 550 } 551 return result; 552 } 553 554 /* 555 * Find a user-defined binding, given the curses key code. If it does not 556 * exist, create a new one, inserting it into the linked list, keeping it 557 * sorted to simplify lookups for user-defined bindings that can override 558 * the built-in bindings. 559 */ 560 static DLG_KEYS_BINDING * 561 make_binding(char *widget, int curses_key, int is_function, int dialog_key) 562 { 563 LIST_BINDINGS *entry = 0; 564 DLG_KEYS_BINDING *data = 0; 565 char *name; 566 LIST_BINDINGS *p, *q; 567 DLG_KEYS_BINDING *result = find_binding(widget, curses_key); 568 569 if (result == 0 570 && (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0 571 && (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0 572 && (name = dlg_strclone(widget)) != 0) { 573 574 entry->name = name; 575 entry->binding = data; 576 577 data[0].is_function_key = is_function; 578 data[0].curses_key = curses_key; 579 data[0].dialog_key = dialog_key; 580 581 data[1] = end_keys_binding; 582 583 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) { 584 if (compare_bindings(entry, p) < 0) { 585 break; 586 } 587 } 588 if (q != 0) { 589 q->link = entry; 590 } else { 591 all_bindings = entry; 592 } 593 if (p != 0) { 594 entry->link = p; 595 } 596 result = data; 597 } else if (entry != 0) { 598 free(entry); 599 if (data) 600 free(data); 601 } 602 603 return result; 604 } 605 606 static int 607 decode_escaped(char **string) 608 { 609 unsigned n; 610 int result = 0; 611 612 if (IsOctal(**string)) { 613 int limit = 3; 614 while (limit-- > 0 && IsOctal(**string)) { 615 int ch = (**string); 616 *string += 1; 617 result = (result << 3) | (ch - '0'); 618 } 619 } else { 620 for (n = 0; n < TableSize(escaped_letters); ++n) { 621 if (**string == escaped_letters[n].letter) { 622 *string += 1; 623 result = escaped_letters[n].actual; 624 break; 625 } 626 } 627 } 628 return result; 629 } 630 631 static char * 632 encode_escaped(int value) 633 { 634 static char result[80]; 635 unsigned n; 636 bool found = FALSE; 637 for (n = 0; n < TableSize(escaped_letters); ++n) { 638 if (value == escaped_letters[n].actual) { 639 found = TRUE; 640 sprintf(result, "%c", escaped_letters[n].letter); 641 break; 642 } 643 } 644 if (!found) { 645 sprintf(result, "%03o", value & 0xff); 646 } 647 return result; 648 } 649 650 /* 651 * Parse the parameters of the "bindkey" configuration-file entry. This 652 * expects widget name which may be "*", followed by curses key definition and 653 * then dialog key definition. 654 * 655 * The curses key "should" be one of the names (ignoring case) from 656 * curses_names[], but may also be a single control character (prefix "^" or 657 * "~" depending on whether it is C0 or C1), or an escaped single character. 658 * Binding a printable character with dialog is possible but not useful. 659 * 660 * The dialog key must be one of the names from dialog_names[]. 661 */ 662 int 663 dlg_parse_bindkey(char *params) 664 { 665 char *p = skip_white(params); 666 char *q; 667 bool escaped = FALSE; 668 int modified = 0; 669 int result = FALSE; 670 unsigned xx; 671 char *widget; 672 int is_function = FALSE; 673 int curses_key; 674 int dialog_key; 675 676 curses_key = -1; 677 dialog_key = -1; 678 widget = p; 679 680 p = skip_black(p); 681 if (p != widget && *p != '\0') { 682 *p++ = '\0'; 683 p = skip_white(p); 684 q = p; 685 while (*p != '\0' && curses_key < 0) { 686 if (escaped) { 687 escaped = FALSE; 688 curses_key = decode_escaped(&p); 689 } else if (*p == CHR_BACKSLASH) { 690 escaped = TRUE; 691 } else if (modified) { 692 if (*p == '?') { 693 curses_key = ((modified == '^') 694 ? 127 695 : 255); 696 } else { 697 curses_key = ((modified == '^') 698 ? (*p & 0x1f) 699 : ((*p & 0x1f) | 0x80)); 700 } 701 } else if (*p == '^') { 702 modified = *p; 703 } else if (*p == '~') { 704 modified = *p; 705 } else if (isspace(UCH(*p))) { 706 break; 707 } 708 ++p; 709 } 710 if (!isspace(UCH(*p))) { 711 ; 712 } else { 713 *p++ = '\0'; 714 if (curses_key < 0) { 715 char fprefix[2]; 716 char check[2]; 717 int keynumber; 718 if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) { 719 curses_key = KEY_F(keynumber); 720 is_function = TRUE; 721 } else { 722 for (xx = 0; xx < COUNT_CURSES; ++xx) { 723 if (!dlg_strcmp(curses_names[xx].name, q)) { 724 curses_key = curses_names[xx].code; 725 is_function = (curses_key >= KEY_MIN); 726 break; 727 } 728 } 729 } 730 } 731 } 732 q = skip_white(p); 733 p = skip_black(q); 734 if (p != q) { 735 for (xx = 0; xx < COUNT_DIALOG; ++xx) { 736 if (!dlg_strcmp(dialog_names[xx].name, q)) { 737 dialog_key = dialog_names[xx].code; 738 break; 739 } 740 } 741 } 742 if (*widget != '\0' 743 && curses_key >= 0 744 && dialog_key >= 0 745 && make_binding(widget, curses_key, is_function, dialog_key) != 0) { 746 result = TRUE; 747 } 748 } 749 return result; 750 } 751 752 static void 753 dump_curses_key(FILE *fp, int curses_key) 754 { 755 if (curses_key > KEY_MIN) { 756 unsigned n; 757 bool found = FALSE; 758 for (n = 0; n < COUNT_CURSES; ++n) { 759 if (curses_names[n].code == curses_key) { 760 fprintf(fp, "%s", curses_names[n].name); 761 found = TRUE; 762 break; 763 } 764 } 765 if (!found) { 766 #ifdef KEY_MOUSE 767 if (is_DLGK_MOUSE(curses_key)) { 768 fprintf(fp, "MOUSE-"); 769 dump_curses_key(fp, curses_key - M_EVENT); 770 } else 771 #endif 772 if (curses_key >= KEY_F(0)) { 773 fprintf(fp, "F%d", curses_key - KEY_F(0)); 774 } else { 775 fprintf(fp, "curses%d", curses_key); 776 } 777 } 778 } else if (curses_key >= 0 && curses_key < 32) { 779 fprintf(fp, "^%c", curses_key + 64); 780 } else if (curses_key == 127) { 781 fprintf(fp, "^?"); 782 } else if (curses_key >= 128 && curses_key < 160) { 783 fprintf(fp, "~%c", curses_key - 64); 784 } else if (curses_key == 255) { 785 fprintf(fp, "~?"); 786 } else if (curses_key > 32 && 787 curses_key < 127 && 788 curses_key != CHR_BACKSLASH) { 789 fprintf(fp, "%c", curses_key); 790 } else { 791 fprintf(fp, "%c%s", CHR_BACKSLASH, encode_escaped(curses_key)); 792 } 793 } 794 795 static void 796 dump_dialog_key(FILE *fp, int dialog_key) 797 { 798 unsigned n; 799 bool found = FALSE; 800 for (n = 0; n < COUNT_DIALOG; ++n) { 801 if (dialog_names[n].code == dialog_key) { 802 fputs(dialog_names[n].name, fp); 803 found = TRUE; 804 break; 805 } 806 } 807 if (!found) { 808 fprintf(fp, "dialog%d", dialog_key); 809 } 810 } 811 812 static void 813 dump_one_binding(FILE *fp, 814 WINDOW *win, 815 const char *widget, 816 DLG_KEYS_BINDING * binding) 817 { 818 int actual; 819 int fkey = (binding->curses_key > 255); 820 821 fprintf(fp, "bindkey %s ", widget); 822 dump_curses_key(fp, binding->curses_key); 823 fputc(' ', fp); 824 dump_dialog_key(fp, binding->dialog_key); 825 actual = dlg_lookup_key(win, binding->curses_key, &fkey); 826 #ifdef KEY_MOUSE 827 if (is_DLGK_MOUSE(binding->curses_key) && is_DLGK_MOUSE(actual)) { 828 ; /* EMPTY */ 829 } else 830 #endif 831 if (actual != binding->dialog_key) { 832 fprintf(fp, "\t# overridden by "); 833 dump_dialog_key(fp, actual); 834 } 835 fputc('\n', fp); 836 } 837 838 /* 839 * Dump bindings for the given window. If it is a null, then this dumps the 840 * initial bindings which were loaded from the rc-file that are used as 841 * overall defaults. 842 */ 843 void 844 dlg_dump_window_keys(FILE *fp, WINDOW *win) 845 { 846 if (fp != 0) { 847 LIST_BINDINGS *p; 848 DLG_KEYS_BINDING *q; 849 const char *last = ""; 850 851 for (p = all_bindings; p != 0; p = p->link) { 852 if (p->win == win) { 853 if (dlg_strcmp(last, p->name)) { 854 fprintf(fp, "# key bindings for %s widgets%s\n", 855 !strcmp(p->name, WILDNAME) ? "all" : p->name, 856 win == 0 ? " (user-defined)" : ""); 857 last = p->name; 858 } 859 for (q = p->binding; q->is_function_key >= 0; ++q) { 860 dump_one_binding(fp, win, p->name, q); 861 } 862 } 863 } 864 } 865 } 866 867 /* 868 * Dump all of the bindings which are not specific to a given widget, i.e., 869 * the "win" member is null. 870 */ 871 void 872 dlg_dump_keys(FILE *fp) 873 { 874 if (fp != 0) { 875 LIST_BINDINGS *p; 876 unsigned count = 0; 877 878 for (p = all_bindings; p != 0; p = p->link) { 879 if (p->win == 0) { 880 ++count; 881 } 882 } 883 if (count != 0) { 884 dlg_dump_window_keys(fp, 0); 885 } 886 } 887 } 888 #endif /* HAVE_RC_FILE */ 889