1 /* 2 * $Id: dlg_keys.c,v 1.35 2014/01/12 18:21:52 tom Exp $ 3 * 4 * dlg_keys.c -- runtime binding support for dialog 5 * 6 * Copyright 2006-2011,2014 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 LIST_BINDINGS { 30 LIST_BINDINGS *link; 31 WINDOW *win; /* window on which widget gets input */ 32 const char *name; /* widget name */ 33 bool buttons; /* true only for dlg_register_buttons() */ 34 DLG_KEYS_BINDING *binding; /* list of bindings */ 35 }; 36 37 #define WILDNAME "*" 38 static LIST_BINDINGS *all_bindings; 39 static const DLG_KEYS_BINDING end_keys_binding = END_KEYS_BINDING; 40 41 /* 42 * For a given named widget's window, associate a binding table. 43 */ 44 void 45 dlg_register_window(WINDOW *win, const char *name, DLG_KEYS_BINDING * binding) 46 { 47 LIST_BINDINGS *p, *q; 48 49 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) { 50 if (p->win == win && !strcmp(p->name, name)) { 51 p->binding = binding; 52 return; 53 } 54 } 55 /* add built-in bindings at the end of the list (see compare_bindings). */ 56 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) { 57 p->win = win; 58 p->name = name; 59 p->binding = binding; 60 if (q != 0) 61 q->link = p; 62 else 63 all_bindings = p; 64 } 65 #if defined(HAVE_DLG_TRACE) && defined(HAVE_RC_FILE) 66 /* 67 * Trace the binding information assigned to this window. For most widgets 68 * there is only one binding table. forms have two, so the trace will be 69 * longer. Since compiled-in bindings are only visible when the widget is 70 * registered, there is no other way to see what bindings are available, 71 * than by running dialog and tracing it. 72 */ 73 dlg_trace_msg("# dlg_register_window %s\n", name); 74 dlg_dump_window_keys(dialog_state.trace_output, win); 75 #endif 76 } 77 78 /* 79 * Unlike dlg_lookup_key(), this looks for either widget-builtin or rc-file 80 * definitions, depending on whether 'win' is null. 81 */ 82 static int 83 key_is_bound(WINDOW *win, const char *name, int curses_key, int function_key) 84 { 85 LIST_BINDINGS *p; 86 87 for (p = all_bindings; p != 0; p = p->link) { 88 if (p->win == win && !dlg_strcmp(p->name, name)) { 89 int n; 90 for (n = 0; p->binding[n].is_function_key >= 0; ++n) { 91 if (p->binding[n].curses_key == curses_key 92 && p->binding[n].is_function_key == function_key) { 93 return TRUE; 94 } 95 } 96 } 97 } 98 return FALSE; 99 } 100 101 /* 102 * Call this function after dlg_register_window(), for the list of button 103 * labels associated with the widget. 104 * 105 * Ensure that dlg_lookup_key() will not accidentally translate a key that 106 * we would like to use for a button abbreviation to some other key, e.g., 107 * h/j/k/l for navigation into a cursor key. Do this by binding the key 108 * to itself. 109 * 110 * See dlg_char_to_button(). 111 */ 112 void 113 dlg_register_buttons(WINDOW *win, const char *name, const char **buttons) 114 { 115 int n; 116 LIST_BINDINGS *p; 117 DLG_KEYS_BINDING *q; 118 119 if (buttons == 0) 120 return; 121 122 for (n = 0; buttons[n] != 0; ++n) { 123 int curses_key = dlg_button_to_char(buttons[n]); 124 125 /* ignore multibyte characters */ 126 if (curses_key >= KEY_MIN) 127 continue; 128 129 /* if it is not bound in the widget, skip it (no conflicts) */ 130 if (!key_is_bound(win, name, curses_key, FALSE)) 131 continue; 132 133 #ifdef HAVE_RC_FILE 134 /* if it is bound in the rc-file, skip it */ 135 if (key_is_bound(0, name, curses_key, FALSE)) 136 continue; 137 #endif 138 139 if ((p = dlg_calloc(LIST_BINDINGS, 1)) != 0) { 140 if ((q = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0) { 141 q[0].is_function_key = 0; 142 q[0].curses_key = curses_key; 143 q[0].dialog_key = curses_key; 144 q[1] = end_keys_binding; 145 146 p->win = win; 147 p->name = name; 148 p->buttons = TRUE; 149 p->binding = q; 150 151 /* put these at the beginning, to override the widget's table */ 152 p->link = all_bindings; 153 all_bindings = p; 154 } else { 155 free(p); 156 } 157 } 158 } 159 } 160 161 /* 162 * Remove the bindings for a given window. 163 */ 164 void 165 dlg_unregister_window(WINDOW *win) 166 { 167 LIST_BINDINGS *p, *q; 168 169 for (p = all_bindings, q = 0; p != 0; p = p->link) { 170 if (p->win == win) { 171 if (q != 0) { 172 q->link = p->link; 173 } else { 174 all_bindings = p->link; 175 } 176 /* the user-defined and buttons-bindings all are length=1 */ 177 if (p->binding[1].is_function_key < 0) 178 free(p->binding); 179 free(p); 180 dlg_unregister_window(win); 181 break; 182 } 183 q = p; 184 } 185 } 186 187 /* 188 * Call this after wgetch(), using the same window pointer and passing 189 * the curses-key. 190 * 191 * If there is no binding associated with the widget, it simply returns 192 * the given curses-key. 193 * 194 * Parameters: 195 * win is the window on which the wgetch() was done. 196 * curses_key is the value returned by wgetch(). 197 * fkey in/out (on input, it is true if curses_key is a function key, 198 * and on output, it is true if the result is a function key). 199 */ 200 int 201 dlg_lookup_key(WINDOW *win, int curses_key, int *fkey) 202 { 203 LIST_BINDINGS *p; 204 DLG_KEYS_BINDING *q; 205 206 /* 207 * Ignore mouse clicks, since they are already encoded properly. 208 */ 209 #ifdef KEY_MOUSE 210 if (*fkey != 0 && curses_key == KEY_MOUSE) { 211 ; 212 } else 213 #endif 214 /* 215 * Ignore resize events, since they are already encoded properly. 216 */ 217 #ifdef KEY_RESIZE 218 if (*fkey != 0 && curses_key == KEY_RESIZE) { 219 ; 220 } else 221 #endif 222 if (*fkey == 0 || curses_key < KEY_MAX) { 223 const char *name = WILDNAME; 224 if (win != 0) { 225 for (p = all_bindings; p != 0; p = p->link) { 226 if (p->win == win) { 227 name = p->name; 228 break; 229 } 230 } 231 } 232 for (p = all_bindings; p != 0; p = p->link) { 233 if (p->win == win || 234 (p->win == 0 && 235 (!strcmp(p->name, name) || !strcmp(p->name, WILDNAME)))) { 236 int function_key = (*fkey != 0); 237 for (q = p->binding; q->is_function_key >= 0; ++q) { 238 if (p->buttons 239 && !function_key 240 && q->curses_key == (int) dlg_toupper(curses_key)) { 241 *fkey = 0; 242 return q->dialog_key; 243 } 244 if (q->curses_key == curses_key 245 && q->is_function_key == function_key) { 246 *fkey = q->dialog_key; 247 return *fkey; 248 } 249 } 250 } 251 } 252 } 253 return curses_key; 254 } 255 256 /* 257 * Test a dialog internal keycode to see if it corresponds to one of the push 258 * buttons on the widget such as "OK". 259 * 260 * This is only useful if there are user-defined key bindings, since there are 261 * no built-in bindings that map directly to DLGK_OK, etc. 262 * 263 * See also dlg_ok_buttoncode(). 264 */ 265 int 266 dlg_result_key(int dialog_key, int fkey GCC_UNUSED, int *resultp) 267 { 268 int done = FALSE; 269 270 #ifdef HAVE_RC_FILE 271 if (fkey) { 272 switch ((DLG_KEYS_ENUM) dialog_key) { 273 case DLGK_OK: 274 *resultp = DLG_EXIT_OK; 275 done = TRUE; 276 break; 277 case DLGK_CANCEL: 278 if (!dialog_vars.nocancel) { 279 *resultp = DLG_EXIT_CANCEL; 280 done = TRUE; 281 } 282 break; 283 case DLGK_EXTRA: 284 if (dialog_vars.extra_button) { 285 *resultp = DLG_EXIT_EXTRA; 286 done = TRUE; 287 } 288 break; 289 case DLGK_HELP: 290 if (dialog_vars.help_button) { 291 *resultp = DLG_EXIT_HELP; 292 done = TRUE; 293 } 294 break; 295 case DLGK_ESC: 296 *resultp = DLG_EXIT_ESC; 297 done = TRUE; 298 break; 299 default: 300 break; 301 } 302 } else 303 #endif 304 if (dialog_key == ESC) { 305 *resultp = DLG_EXIT_ESC; 306 done = TRUE; 307 } else if (dialog_key == ERR) { 308 *resultp = DLG_EXIT_ERROR; 309 done = TRUE; 310 } 311 312 return done; 313 } 314 315 #ifdef HAVE_RC_FILE 316 typedef struct { 317 const char *name; 318 int code; 319 } CODENAME; 320 321 #define ASCII_NAME(name,code) { #name, code } 322 #define CURSES_NAME(upper) { #upper, KEY_ ## upper } 323 #define COUNT_CURSES sizeof(curses_names)/sizeof(curses_names[0]) 324 static const CODENAME curses_names[] = 325 { 326 ASCII_NAME(ESC, '\033'), 327 ASCII_NAME(CR, '\r'), 328 ASCII_NAME(LF, '\n'), 329 ASCII_NAME(FF, '\f'), 330 ASCII_NAME(TAB, '\t'), 331 ASCII_NAME(DEL, '\177'), 332 333 CURSES_NAME(DOWN), 334 CURSES_NAME(UP), 335 CURSES_NAME(LEFT), 336 CURSES_NAME(RIGHT), 337 CURSES_NAME(HOME), 338 CURSES_NAME(BACKSPACE), 339 CURSES_NAME(F0), 340 CURSES_NAME(DL), 341 CURSES_NAME(IL), 342 CURSES_NAME(DC), 343 CURSES_NAME(IC), 344 CURSES_NAME(EIC), 345 CURSES_NAME(CLEAR), 346 CURSES_NAME(EOS), 347 CURSES_NAME(EOL), 348 CURSES_NAME(SF), 349 CURSES_NAME(SR), 350 CURSES_NAME(NPAGE), 351 CURSES_NAME(PPAGE), 352 CURSES_NAME(STAB), 353 CURSES_NAME(CTAB), 354 CURSES_NAME(CATAB), 355 CURSES_NAME(ENTER), 356 CURSES_NAME(PRINT), 357 CURSES_NAME(LL), 358 CURSES_NAME(A1), 359 CURSES_NAME(A3), 360 CURSES_NAME(B2), 361 CURSES_NAME(C1), 362 CURSES_NAME(C3), 363 CURSES_NAME(BTAB), 364 CURSES_NAME(BEG), 365 CURSES_NAME(CANCEL), 366 CURSES_NAME(CLOSE), 367 CURSES_NAME(COMMAND), 368 CURSES_NAME(COPY), 369 CURSES_NAME(CREATE), 370 CURSES_NAME(END), 371 CURSES_NAME(EXIT), 372 CURSES_NAME(FIND), 373 CURSES_NAME(HELP), 374 CURSES_NAME(MARK), 375 CURSES_NAME(MESSAGE), 376 CURSES_NAME(MOVE), 377 CURSES_NAME(NEXT), 378 CURSES_NAME(OPEN), 379 CURSES_NAME(OPTIONS), 380 CURSES_NAME(PREVIOUS), 381 CURSES_NAME(REDO), 382 CURSES_NAME(REFERENCE), 383 CURSES_NAME(REFRESH), 384 CURSES_NAME(REPLACE), 385 CURSES_NAME(RESTART), 386 CURSES_NAME(RESUME), 387 CURSES_NAME(SAVE), 388 CURSES_NAME(SBEG), 389 CURSES_NAME(SCANCEL), 390 CURSES_NAME(SCOMMAND), 391 CURSES_NAME(SCOPY), 392 CURSES_NAME(SCREATE), 393 CURSES_NAME(SDC), 394 CURSES_NAME(SDL), 395 CURSES_NAME(SELECT), 396 CURSES_NAME(SEND), 397 CURSES_NAME(SEOL), 398 CURSES_NAME(SEXIT), 399 CURSES_NAME(SFIND), 400 CURSES_NAME(SHELP), 401 CURSES_NAME(SHOME), 402 CURSES_NAME(SIC), 403 CURSES_NAME(SLEFT), 404 CURSES_NAME(SMESSAGE), 405 CURSES_NAME(SMOVE), 406 CURSES_NAME(SNEXT), 407 CURSES_NAME(SOPTIONS), 408 CURSES_NAME(SPREVIOUS), 409 CURSES_NAME(SPRINT), 410 CURSES_NAME(SREDO), 411 CURSES_NAME(SREPLACE), 412 CURSES_NAME(SRIGHT), 413 CURSES_NAME(SRSUME), 414 CURSES_NAME(SSAVE), 415 CURSES_NAME(SSUSPEND), 416 CURSES_NAME(SUNDO), 417 CURSES_NAME(SUSPEND), 418 CURSES_NAME(UNDO), 419 }; 420 421 #define DIALOG_NAME(upper) { #upper, DLGK_ ## upper } 422 #define COUNT_DIALOG sizeof(dialog_names)/sizeof(dialog_names[0]) 423 static const CODENAME dialog_names[] = 424 { 425 DIALOG_NAME(OK), 426 DIALOG_NAME(CANCEL), 427 DIALOG_NAME(EXTRA), 428 DIALOG_NAME(HELP), 429 DIALOG_NAME(ESC), 430 DIALOG_NAME(PAGE_FIRST), 431 DIALOG_NAME(PAGE_LAST), 432 DIALOG_NAME(PAGE_NEXT), 433 DIALOG_NAME(PAGE_PREV), 434 DIALOG_NAME(ITEM_FIRST), 435 DIALOG_NAME(ITEM_LAST), 436 DIALOG_NAME(ITEM_NEXT), 437 DIALOG_NAME(ITEM_PREV), 438 DIALOG_NAME(FIELD_FIRST), 439 DIALOG_NAME(FIELD_LAST), 440 DIALOG_NAME(FIELD_NEXT), 441 DIALOG_NAME(FIELD_PREV), 442 DIALOG_NAME(FORM_FIRST), 443 DIALOG_NAME(FORM_LAST), 444 DIALOG_NAME(FORM_NEXT), 445 DIALOG_NAME(FORM_PREV), 446 DIALOG_NAME(GRID_UP), 447 DIALOG_NAME(GRID_DOWN), 448 DIALOG_NAME(GRID_LEFT), 449 DIALOG_NAME(GRID_RIGHT), 450 DIALOG_NAME(DELETE_LEFT), 451 DIALOG_NAME(DELETE_RIGHT), 452 DIALOG_NAME(DELETE_ALL), 453 DIALOG_NAME(ENTER), 454 DIALOG_NAME(BEGIN), 455 DIALOG_NAME(FINAL), 456 DIALOG_NAME(SELECT), 457 DIALOG_NAME(HELPFILE), 458 DIALOG_NAME(TRACE) 459 }; 460 461 static char * 462 skip_white(char *s) 463 { 464 while (*s != '\0' && isspace(UCH(*s))) 465 ++s; 466 return s; 467 } 468 469 static char * 470 skip_black(char *s) 471 { 472 while (*s != '\0' && !isspace(UCH(*s))) 473 ++s; 474 return s; 475 } 476 477 /* 478 * Find a user-defined binding, given the curses key code. 479 */ 480 static DLG_KEYS_BINDING * 481 find_binding(char *widget, int curses_key) 482 { 483 LIST_BINDINGS *p; 484 DLG_KEYS_BINDING *result = 0; 485 486 for (p = all_bindings; p != 0; p = p->link) { 487 if (p->win == 0 488 && !dlg_strcmp(p->name, widget) 489 && p->binding->curses_key == curses_key) { 490 result = p->binding; 491 break; 492 } 493 } 494 return result; 495 } 496 497 /* 498 * Built-in bindings have a nonzero "win" member, and the associated binding 499 * table can have more than one entry. We keep those last, since lookups will 500 * find the user-defined bindings first and use those. 501 * 502 * Sort "*" (all-widgets) entries past named widgets, since those are less 503 * specific. 504 */ 505 static int 506 compare_bindings(LIST_BINDINGS * a, LIST_BINDINGS * b) 507 { 508 int result = 0; 509 if (a->win == b->win) { 510 if (!strcmp(a->name, b->name)) { 511 result = a->binding[0].curses_key - b->binding[0].curses_key; 512 } else if (!strcmp(b->name, WILDNAME)) { 513 result = -1; 514 } else if (!strcmp(a->name, WILDNAME)) { 515 result = 1; 516 } else { 517 result = dlg_strcmp(a->name, b->name); 518 } 519 } else if (b->win) { 520 result = -1; 521 } else { 522 result = 1; 523 } 524 return result; 525 } 526 527 /* 528 * Find a user-defined binding, given the curses key code. If it does not 529 * exist, create a new one, inserting it into the linked list, keeping it 530 * sorted to simplify lookups for user-defined bindings that can override 531 * the built-in bindings. 532 */ 533 static DLG_KEYS_BINDING * 534 make_binding(char *widget, int curses_key, int is_function, int dialog_key) 535 { 536 LIST_BINDINGS *entry = 0; 537 DLG_KEYS_BINDING *data = 0; 538 char *name; 539 LIST_BINDINGS *p, *q; 540 DLG_KEYS_BINDING *result = find_binding(widget, curses_key); 541 542 if (result == 0 543 && (entry = dlg_calloc(LIST_BINDINGS, 1)) != 0 544 && (data = dlg_calloc(DLG_KEYS_BINDING, 2)) != 0 545 && (name = dlg_strclone(widget)) != 0) { 546 547 entry->name = name; 548 entry->binding = data; 549 550 data[0].is_function_key = is_function; 551 data[0].curses_key = curses_key; 552 data[0].dialog_key = dialog_key; 553 554 data[1] = end_keys_binding; 555 556 for (p = all_bindings, q = 0; p != 0; q = p, p = p->link) { 557 if (compare_bindings(entry, p) < 0) { 558 break; 559 } 560 } 561 if (q != 0) { 562 q->link = entry; 563 } else { 564 all_bindings = entry; 565 } 566 if (p != 0) { 567 entry->link = p; 568 } 569 result = data; 570 } else if (entry != 0) { 571 free(entry); 572 if (data) 573 free(data); 574 } 575 576 return result; 577 } 578 579 /* 580 * Parse the parameters of the "bindkeys" configuration-file entry. This 581 * expects widget name which may be "*", followed by curses key definition and 582 * then dialog key definition. 583 * 584 * The curses key "should" be one of the names (ignoring case) from 585 * curses_names[], but may also be a single control character (prefix "^" or 586 * "~" depending on whether it is C0 or C1), or an escaped single character. 587 * Binding a printable character with dialog is possible but not useful. 588 * 589 * The dialog key must be one of the names from dialog_names[]. 590 */ 591 int 592 dlg_parse_bindkey(char *params) 593 { 594 char *p = skip_white(params); 595 char *q; 596 bool escaped = FALSE; 597 int modified = 0; 598 int result = FALSE; 599 unsigned xx; 600 char *widget; 601 int is_function = FALSE; 602 int curses_key; 603 int dialog_key; 604 605 curses_key = -1; 606 dialog_key = -1; 607 widget = p; 608 609 p = skip_black(p); 610 if (p != widget && *p != '\0') { 611 *p++ = '\0'; 612 p = skip_white(p); 613 q = p; 614 while (*p != '\0' && curses_key < 0) { 615 if (escaped) { 616 escaped = FALSE; 617 curses_key = *p; 618 } else if (*p == '\\') { 619 escaped = TRUE; 620 } else if (modified) { 621 if (*p == '?') { 622 curses_key = ((modified == '^') 623 ? 127 624 : 255); 625 } else { 626 curses_key = ((modified == '^') 627 ? (*p & 0x1f) 628 : ((*p & 0x1f) | 0x80)); 629 } 630 } else if (*p == '^') { 631 modified = *p; 632 } else if (*p == '~') { 633 modified = *p; 634 } else if (isspace(UCH(*p))) { 635 break; 636 } 637 ++p; 638 } 639 if (!isspace(UCH(*p))) { 640 ; 641 } else { 642 *p++ = '\0'; 643 if (curses_key < 0) { 644 char fprefix[2]; 645 char check[2]; 646 int keynumber; 647 if (sscanf(q, "%[Ff]%d%c", fprefix, &keynumber, check) == 2) { 648 curses_key = KEY_F(keynumber); 649 is_function = TRUE; 650 } else { 651 for (xx = 0; xx < COUNT_CURSES; ++xx) { 652 if (!dlg_strcmp(curses_names[xx].name, q)) { 653 curses_key = curses_names[xx].code; 654 is_function = (curses_key >= KEY_MIN); 655 break; 656 } 657 } 658 } 659 } 660 } 661 q = skip_white(p); 662 p = skip_black(q); 663 if (p != q) { 664 for (xx = 0; xx < COUNT_DIALOG; ++xx) { 665 if (!dlg_strcmp(dialog_names[xx].name, q)) { 666 dialog_key = dialog_names[xx].code; 667 break; 668 } 669 } 670 } 671 if (*widget != '\0' 672 && curses_key >= 0 673 && dialog_key >= 0 674 && make_binding(widget, curses_key, is_function, dialog_key) != 0) { 675 result = TRUE; 676 } 677 } 678 return result; 679 } 680 681 static void 682 dump_curses_key(FILE *fp, int curses_key) 683 { 684 if (curses_key > KEY_MIN) { 685 unsigned n; 686 bool found = FALSE; 687 for (n = 0; n < COUNT_CURSES; ++n) { 688 if (curses_names[n].code == curses_key) { 689 fprintf(fp, "%s", curses_names[n].name); 690 found = TRUE; 691 break; 692 } 693 } 694 if (!found) { 695 if (curses_key >= KEY_F(0)) { 696 fprintf(fp, "F%d", curses_key - KEY_F(0)); 697 } else { 698 fprintf(fp, "curses%d", curses_key); 699 } 700 } 701 } else if (curses_key >= 0 && curses_key < 32) { 702 fprintf(fp, "^%c", curses_key + 64); 703 } else if (curses_key == 127) { 704 fprintf(fp, "^?"); 705 } else if (curses_key >= 128 && curses_key < 160) { 706 fprintf(fp, "~%c", curses_key - 64); 707 } else if (curses_key == 255) { 708 fprintf(fp, "~?"); 709 } else { 710 fprintf(fp, "\\%c", curses_key); 711 } 712 } 713 714 static void 715 dump_dialog_key(FILE *fp, int dialog_key) 716 { 717 unsigned n; 718 bool found = FALSE; 719 for (n = 0; n < COUNT_DIALOG; ++n) { 720 if (dialog_names[n].code == dialog_key) { 721 fputs(dialog_names[n].name, fp); 722 found = TRUE; 723 break; 724 } 725 } 726 if (!found) { 727 fprintf(fp, "dialog%d", dialog_key); 728 } 729 } 730 731 static void 732 dump_one_binding(FILE *fp, const char *widget, DLG_KEYS_BINDING * binding) 733 { 734 fprintf(fp, "bindkey %s ", widget); 735 dump_curses_key(fp, binding->curses_key); 736 fputc(' ', fp); 737 dump_dialog_key(fp, binding->dialog_key); 738 fputc('\n', fp); 739 } 740 741 /* 742 * Dump bindings for the given window. If it is a null, then this dumps the 743 * initial bindings which were loaded from the rc-file that are used as 744 * overall defaults. 745 */ 746 void 747 dlg_dump_window_keys(FILE *fp, WINDOW *win) 748 { 749 if (fp != 0) { 750 LIST_BINDINGS *p; 751 DLG_KEYS_BINDING *q; 752 const char *last = ""; 753 754 for (p = all_bindings; p != 0; p = p->link) { 755 if (p->win == win) { 756 if (dlg_strcmp(last, p->name)) { 757 fprintf(fp, "\n# key bindings for %s widgets\n", 758 !strcmp(p->name, WILDNAME) ? "all" : p->name); 759 last = p->name; 760 } 761 for (q = p->binding; q->is_function_key >= 0; ++q) { 762 dump_one_binding(fp, p->name, q); 763 } 764 } 765 } 766 } 767 } 768 769 /* 770 * Dump all of the bindings which are not specific to a given widget, i.e., 771 * the "win" member is null. 772 */ 773 void 774 dlg_dump_keys(FILE *fp) 775 { 776 if (fp != 0) { 777 LIST_BINDINGS *p; 778 unsigned count = 0; 779 780 for (p = all_bindings; p != 0; p = p->link) { 781 if (p->win == 0) { 782 ++count; 783 } 784 } 785 if (count != 0) { 786 dlg_dump_window_keys(fp, 0); 787 } 788 } 789 } 790 #endif /* HAVE_RC_FILE */ 791