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