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