Make struct action const
[elinks.git] / src / config / kbdbind.c
blob9fb00e91f547351f5ae0c522e102e54a11280f7e
1 /* Keybinding implementation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <ctype.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "bfu/dialog.h"
13 #include "config/conf.h"
14 #include "config/dialogs.h"
15 #include "config/kbdbind.h"
16 #include "config/options.h"
17 #include "intl/gettext/libintl.h"
18 #include "main/event.h"
19 #include "main/module.h"
20 #include "terminal/kbd.h"
21 #include "util/conv.h"
22 #include "util/memory.h"
23 #include "util/string.h"
26 /* Fix namespace clash on MacOS. */
27 #define table table_elinks
29 static const struct action_list action_table[KEYMAP_MAX];
30 static struct keymap keymap_table[KEYMAP_MAX];
31 static LIST_OF(struct keybinding) keymaps[KEYMAP_MAX];
33 static void add_default_keybindings(void);
35 static int
36 delete_keybinding(enum keymap_id keymap_id, struct term_event_keyboard *kbd)
38 struct keybinding *keybinding;
40 foreach (keybinding, keymaps[keymap_id]) {
41 int was_default = 0;
43 if (!kbd_key_is(&keybinding->kbd, kbd->key))
44 continue;
46 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
47 continue;
50 if (keybinding->flags & KBDB_DEFAULT_KEY) {
51 keybinding->flags &= ~KBDB_DEFAULT_KEY;
52 was_default = 1;
55 free_keybinding(keybinding);
57 return 1 + was_default;
60 return 0;
63 static int keybinding_is_default(struct keybinding *keybinding);
65 struct keybinding *
66 add_keybinding(enum keymap_id keymap_id, action_id_T action_id,
67 struct term_event_keyboard *kbd, int event)
69 struct keybinding *keybinding;
70 struct listbox_item *root;
71 int is_default;
73 is_default = (delete_keybinding(keymap_id, kbd) == 2);
75 keybinding = mem_calloc(1, sizeof(*keybinding));
76 if (!keybinding) return NULL;
78 keybinding->keymap_id = keymap_id;
79 keybinding->action_id = action_id;
80 copy_struct(&keybinding->kbd, kbd);
81 keybinding->event = event;
82 keybinding->flags = is_default * KBDB_DEFAULT_KEY;
83 if (keybinding_is_default(keybinding))
84 keybinding->flags |= KBDB_DEFAULT_BINDING;
86 object_nolock(keybinding, "keybinding");
87 add_to_list(keymaps[keymap_id], keybinding);
89 if (action_id == ACT_MAIN_NONE) {
90 /* We don't want such a listbox_item, do we? */
91 return NULL; /* Or goto. */
94 root = get_keybinding_action_box_item(keymap_id, action_id);
95 if (!root) {
96 return NULL; /* Or goto ;-). */
98 keybinding->box_item = add_listbox_leaf(&keybinding_browser, root, keybinding);
100 return keybinding;
103 void
104 free_keybinding(struct keybinding *keybinding)
106 if (keybinding->box_item) {
107 done_listbox_item(&keybinding_browser, keybinding->box_item);
108 keybinding->box_item = NULL;
111 #ifdef CONFIG_SCRIPTING
112 /* TODO: unref function must be implemented. This is part of bug 810. */
113 /* if (keybinding->event != EVENT_NONE)
114 scripting_unref(keybinding->event); */
115 #endif
117 if (keybinding->flags & KBDB_DEFAULT_KEY) {
118 /* We cannot just delete a default keybinding, instead we have
119 * to rebind it to ACT_MAIN_NONE so that it gets written so to the
120 * config file. */
121 keybinding->flags &= ~KBDB_DEFAULT_BINDING;
122 keybinding->action_id = ACT_MAIN_NONE;
123 return;
126 del_from_list(keybinding);
127 mem_free(keybinding);
131 keybinding_exists(enum keymap_id keymap_id, struct term_event_keyboard *kbd,
132 action_id_T *action_id)
134 struct keybinding *keybinding;
136 foreach (keybinding, keymaps[keymap_id]) {
137 if (!kbd_key_is(&keybinding->kbd, kbd->key))
138 continue;
140 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
141 continue;
143 if (action_id) *action_id = keybinding->action_id;
145 return 1;
148 return 0;
152 action_id_T
153 kbd_action(enum keymap_id keymap_id, struct term_event *ev, int *event)
155 struct keybinding *keybinding;
157 if (ev->ev != EVENT_KBD) return -1;
159 keybinding = kbd_ev_lookup(keymap_id, &ev->info.keyboard, event);
160 return keybinding ? keybinding->action_id : -1;
163 struct keybinding *
164 kbd_ev_lookup(enum keymap_id keymap_id, struct term_event_keyboard *kbd, int *event)
166 struct keybinding *keybinding;
168 foreach (keybinding, keymaps[keymap_id]) {
169 if (!kbd_key_is(&keybinding->kbd, kbd->key))
170 continue;
172 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
173 continue;
175 if (keybinding->action_id == ACT_MAIN_SCRIPTING_FUNCTION && event)
176 *event = keybinding->event;
178 return keybinding;
181 return NULL;
184 static struct keybinding *
185 kbd_act_lookup(enum keymap_id keymap_id, action_id_T action_id)
187 struct keybinding *keybinding;
189 foreach (keybinding, keymaps[keymap_id]) {
190 if (action_id != keybinding->action_id)
191 continue;
193 return keybinding;
196 return NULL;
199 struct keybinding *
200 kbd_nm_lookup(enum keymap_id keymap_id, unsigned char *name)
202 action_id_T action_id = get_action_from_string(keymap_id, name);
204 if (action_id < 0) return NULL;
206 return kbd_act_lookup(keymap_id, action_id);
209 static struct keybinding *
210 kbd_stroke_lookup(enum keymap_id keymap_id, const unsigned char *keystroke_str)
212 struct term_event_keyboard kbd;
214 if (parse_keystroke(keystroke_str, &kbd) < 0)
215 return NULL;
217 return kbd_ev_lookup(keymap_id, &kbd, NULL);
221 static struct keymap keymap_table[] = {
222 { "main", KEYMAP_MAIN, N_("Main mapping") },
223 { "edit", KEYMAP_EDIT, N_("Edit mapping") },
224 { "menu", KEYMAP_MENU, N_("Menu mapping") },
229 * Config file helpers.
232 static const struct action *
233 get_action_from_keystroke(enum keymap_id keymap_id,
234 const unsigned char *keystroke_str)
236 struct keybinding *keybinding = kbd_stroke_lookup(keymap_id,
237 keystroke_str);
239 return keybinding ? get_action(keymap_id, keybinding->action_id) : NULL;
242 unsigned char *
243 get_action_name_from_keystroke(enum keymap_id keymap_id,
244 const unsigned char *keystroke_str)
246 const struct action *action = get_action_from_keystroke(keymap_id,
247 keystroke_str);
249 return action ? action->str : NULL;
252 action_id_T
253 get_action_from_string(enum keymap_id keymap_id, unsigned char *str)
255 const struct action *action;
257 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
259 for (action = action_table[keymap_id].actions; action->str; action++)
260 if (!strcmp(action->str, str))
261 return action->num;
263 return -1;
266 const struct action *
267 get_action(enum keymap_id keymap_id, action_id_T action_id)
269 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
271 if (action_id >= 0 && action_id < action_table[keymap_id].num_actions)
272 return &action_table[keymap_id].actions[action_id];
274 return NULL;
277 unsigned char *
278 get_action_name(enum keymap_id keymap_id, action_id_T action_id)
280 const struct action *action = get_action(keymap_id, action_id);
282 return action ? action->str : NULL;
285 static unsigned char *
286 get_action_desc(enum keymap_id keymap_id, action_id_T action_id)
288 const struct action *action = get_action(keymap_id, action_id);
290 return action ? (action->desc ? action->desc : action->str)
291 : NULL;
295 static struct keymap *
296 get_keymap(enum keymap_id keymap_id)
298 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
300 return &keymap_table[keymap_id];
303 static enum keymap_id
304 get_keymap_id(unsigned char *keymap_str)
306 enum keymap_id keymap_id;
308 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
309 if (!strcmp(keymap_table[keymap_id].str, keymap_str))
310 return keymap_id;
312 return KEYMAP_INVALID;
315 unsigned char *
316 get_keymap_name(enum keymap_id keymap_id)
318 return get_keymap(keymap_id)->str;
322 struct named_key {
323 const unsigned char *str;
324 term_event_key_T num;
327 static const struct named_key key_table[] = {
328 { "Enter", KBD_ENTER },
329 { "Space", ' ' },
330 { "Backspace", KBD_BS },
331 { "Tab", KBD_TAB },
332 { "Escape", KBD_ESC },
333 { "Left", KBD_LEFT },
334 { "Right", KBD_RIGHT },
335 { "Up", KBD_UP },
336 { "Down", KBD_DOWN },
337 { "Insert", KBD_INS },
338 { "Delete", KBD_DEL },
339 { "Home", KBD_HOME },
340 { "End", KBD_END },
341 { "PageUp", KBD_PAGE_UP },
342 { "PageDown", KBD_PAGE_DOWN },
343 { "F1", KBD_F1 },
344 { "F2", KBD_F2 },
345 { "F3", KBD_F3 },
346 { "F4", KBD_F4 },
347 { "F5", KBD_F5 },
348 { "F6", KBD_F6 },
349 { "F7", KBD_F7 },
350 { "F8", KBD_F8 },
351 { "F9", KBD_F9 },
352 { "F10", KBD_F10 },
353 { "F11", KBD_F11 },
354 { "F12", KBD_F12 },
355 { NULL, KBD_UNDEF }
358 term_event_key_T
359 read_key(const unsigned char *key_str)
361 const struct named_key *key;
363 if (key_str[0] && !key_str[1])
364 return key_str[0];
366 for (key = key_table; key->str; key++)
367 if (!c_strcasecmp(key->str, key_str))
368 return key->num;
370 return KBD_UNDEF;
373 /* Parse the string @s as the name of a keystroke.
374 * Write the parsed key and modifiers to *@kbd.
375 * Return >=0 on success, <0 on error. */
377 parse_keystroke(const unsigned char *s, struct term_event_keyboard *kbd)
379 kbd->modifier = KBD_MOD_NONE;
380 while (1) {
381 if (!c_strncasecmp(s, "Shift", 5) && (s[5] == '-' || s[5] == '+')) {
382 /* Shift+a == shiFt-a == Shift-a */
383 kbd->modifier |= KBD_MOD_SHIFT;
384 s += 6;
386 } else if (!c_strncasecmp(s, "Ctrl", 4) && (s[4] == '-' || s[4] == '+')) {
387 /* Ctrl+a == ctRl-a == Ctrl-a */
388 kbd->modifier |= KBD_MOD_CTRL;
389 s += 5;
391 } else if (!c_strncasecmp(s, "Alt", 3) && (s[3] == '-' || s[3] == '+')) {
392 /* Alt+a == aLt-a == Alt-a */
393 kbd->modifier |= KBD_MOD_ALT;
394 s += 4;
396 } else {
397 /* No modifier. */
398 break;
402 kbd->key = read_key(s);
404 if ((kbd->modifier & KBD_MOD_CTRL) != 0
405 && is_kbd_character(kbd->key) && kbd->key <= 0x7F) {
406 /* Convert Ctrl-letter keystrokes to upper case,
407 * e.g. Ctrl-a to Ctrl-A. Do this because terminals
408 * typically generate the same ASCII control
409 * characters regardless of Shift and Caps Lock.
411 * However, that applies only to ASCII letters. For
412 * other Ctrl-letter combinations, there seems to be
413 * no standard of what the terminal should generate.
414 * Because it is possible that the terminal does not
415 * fold case then, ELinks should treat upper-case and
416 * lower-case variants of such keystrokes as different
417 * and let the user bind them to separate actions.
418 * Besides, toupper() might not understand the charset
419 * of kbd->key.
421 * FIXME: Actually, it is possible that some terminals
422 * preserve case in all Ctrl-letter keystrokes, even
423 * for ASCII letters. In particular, the Win32
424 * console API looks like it might do this. When the
425 * terminal handling part of ELinks is eventually
426 * extended to fully support that, it could be a good
427 * idea to change parse_keystroke() not to fold case,
428 * and instead make kbd_ev_lookup() or its callers
429 * search for different variants of the keystroke if
430 * the original one is not bound to any action. */
431 kbd->key = c_toupper(kbd->key);
434 return (kbd->key == KBD_UNDEF) ? -1 : 0;
437 void
438 add_keystroke_to_string(struct string *str, struct term_event_keyboard *kbd,
439 int escape)
441 unsigned char key_buffer[3] = "\\x";
442 const unsigned char *key_string = NULL;
443 const struct named_key *key;
445 if (kbd->key == KBD_UNDEF) return;
447 if (kbd->modifier & KBD_MOD_SHIFT)
448 add_to_string(str, "Shift-");
449 if (kbd->modifier & KBD_MOD_CTRL)
450 add_to_string(str, "Ctrl-");
451 if (kbd->modifier & KBD_MOD_ALT)
452 add_to_string(str, "Alt-");
454 for (key = key_table; key->str; key++) {
455 if (kbd->key == key->num) {
456 key_string = key->str;
457 break;
461 if (!key_string) {
462 key_string = key_buffer + 1;
463 key_buffer[1] = (unsigned char) kbd->key;
464 if (escape && strchr("'\"\\", kbd->key))
465 key_string--;
468 add_to_string(str, key_string);
471 void
472 add_keystroke_action_to_string(struct string *string, action_id_T action_id,
473 enum keymap_id keymap_id)
475 struct keybinding *keybinding = kbd_act_lookup(keymap_id, action_id);
477 if (keybinding)
478 add_keystroke_to_string(string, &keybinding->kbd, 0);
481 unsigned char *
482 get_keystroke(action_id_T action_id, enum keymap_id keymap_id)
484 struct string keystroke;
486 if (!init_string(&keystroke)) return NULL;
488 add_keystroke_action_to_string(&keystroke, action_id, keymap_id);
490 /* Never return empty string */
491 if (!keystroke.length) done_string(&keystroke);
493 return keystroke.source;
496 void
497 add_actions_to_string(struct string *string, action_id_T action_ids[],
498 enum keymap_id keymap_id, struct terminal *term)
500 int i;
502 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
504 add_format_to_string(string, "%s:\n", _(keymap_table[keymap_id].desc, term));
506 for (i = 0; action_ids[i] != ACT_MAIN_NONE; i++) {
507 struct keybinding *keybinding = kbd_act_lookup(keymap_id, action_ids[i]);
508 int keystrokelen = string->length;
509 unsigned char *desc = get_action_desc(keymap_id, action_ids[i]);
511 if (!keybinding) continue;
513 add_char_to_string(string, '\n');
514 add_keystroke_to_string(string, &keybinding->kbd, 0);
515 keystrokelen = string->length - keystrokelen;
516 add_xchar_to_string(string, ' ', int_max(15 - keystrokelen, 1));
517 add_to_string(string, _(desc, term));
521 #define ACTION_(map, name, action, caption, flags) \
522 { name, ACT_##map##_##action, KEYMAP_ID, caption, flags }
524 #undef KEYMAP_ID
525 #define KEYMAP_ID KEYMAP_MAIN
526 static const struct action main_action_table[MAIN_ACTIONS + 1] = {
527 #include "config/actions-main.inc"
530 #undef KEYMAP_ID
531 #define KEYMAP_ID KEYMAP_EDIT
532 static const struct action edit_action_table[EDIT_ACTIONS + 1] = {
533 #include "config/actions-edit.inc"
536 #undef KEYMAP_ID
537 #define KEYMAP_ID KEYMAP_MENU
538 static const struct action menu_action_table[MENU_ACTIONS + 1] = {
539 #include "config/actions-menu.inc"
542 static const struct action_list action_table[KEYMAP_MAX] = {
543 { main_action_table, sizeof_array(main_action_table) },
544 { edit_action_table, sizeof_array(edit_action_table) },
545 { menu_action_table, sizeof_array(menu_action_table) },
548 #undef KEYMAP_ID
549 #undef ACTION_
552 static void
553 init_keymaps(struct module *xxx)
555 enum keymap_id keymap_id;
557 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
558 init_list(keymaps[keymap_id]);
560 init_keybinding_listboxes(keymap_table, action_table);
561 add_default_keybindings();
564 static void
565 free_keymaps(struct module *xxx)
567 enum keymap_id keymap_id;
569 done_keybinding_listboxes();
571 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
572 free_list(keymaps[keymap_id]);
577 * Bind to Lua function.
580 #ifdef CONFIG_SCRIPTING
581 static unsigned char *
582 bind_key_to_event(unsigned char *ckmap, const unsigned char *ckey, int event)
584 struct term_event_keyboard kbd;
585 action_id_T action_id;
586 enum keymap_id keymap_id = get_keymap_id(ckmap);
588 if (keymap_id == KEYMAP_INVALID)
589 return gettext("Unrecognised keymap");
591 if (parse_keystroke(ckey, &kbd) < 0)
592 return gettext("Error parsing keystroke");
594 action_id = get_action_from_string(keymap_id, " *scripting-function*");
595 if (action_id < 0)
596 return gettext("Unrecognised action (internal error)");
598 add_keybinding(keymap_id, action_id, &kbd, event);
600 return NULL;
604 bind_key_to_event_name(unsigned char *ckmap, const unsigned char *ckey,
605 unsigned char *event_name, unsigned char **err)
607 int event_id;
609 event_id = register_event(event_name);
611 if (event_id == EVENT_NONE) {
612 *err = gettext("Error registering event");
613 return EVENT_NONE;
616 *err = bind_key_to_event(ckmap, ckey, event_id);
618 return event_id;
620 #endif
624 * Default keybindings.
627 struct default_kb {
628 struct term_event_keyboard kbd;
629 action_id_T action_id;
632 static struct default_kb default_main_keymap[] = {
633 { { ' ', KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_DOWN },
634 { { '#', KBD_MOD_NONE }, ACT_MAIN_SEARCH_TYPEAHEAD },
635 { { '%', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_DOCUMENT_COLORS },
636 { { '*', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_DISPLAY_IMAGES },
637 { { ',', KBD_MOD_NONE }, ACT_MAIN_LUA_CONSOLE },
638 { { '.', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_NUMBERED_LINKS },
639 { { '/', KBD_MOD_NONE }, ACT_MAIN_SEARCH },
640 { { ':', KBD_MOD_NONE }, ACT_MAIN_EXMODE },
641 { { '<', KBD_MOD_NONE }, ACT_MAIN_TAB_PREV },
642 { { '<', KBD_MOD_ALT }, ACT_MAIN_TAB_MOVE_LEFT },
643 { { '=', KBD_MOD_NONE }, ACT_MAIN_DOCUMENT_INFO },
644 { { '>', KBD_MOD_NONE }, ACT_MAIN_TAB_NEXT },
645 { { '>', KBD_MOD_ALT }, ACT_MAIN_TAB_MOVE_RIGHT },
646 { { '?', KBD_MOD_NONE }, ACT_MAIN_SEARCH_BACK },
647 { { 'A', KBD_MOD_NONE }, ACT_MAIN_ADD_BOOKMARK_LINK },
648 { { 'A', KBD_MOD_CTRL }, ACT_MAIN_MOVE_DOCUMENT_START },
649 { { 'B', KBD_MOD_CTRL }, ACT_MAIN_MOVE_PAGE_UP },
650 { { 'C', KBD_MOD_NONE }, ACT_MAIN_CACHE_MANAGER },
651 { { 'D', KBD_MOD_NONE }, ACT_MAIN_DOWNLOAD_MANAGER },
652 { { 'E', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_CURRENT_LINK },
653 { { 'E', KBD_MOD_CTRL }, ACT_MAIN_MOVE_DOCUMENT_END },
654 { { 'F', KBD_MOD_NONE }, ACT_MAIN_FORMHIST_MANAGER },
655 { { 'F', KBD_MOD_CTRL }, ACT_MAIN_MOVE_PAGE_DOWN },
656 { { 'G', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_CURRENT },
657 { { 'H', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_HOME },
658 { { 'K', KBD_MOD_NONE }, ACT_MAIN_COOKIE_MANAGER },
659 { { 'K', KBD_MOD_CTRL }, ACT_MAIN_COOKIES_LOAD },
660 { { 'L', KBD_MOD_NONE }, ACT_MAIN_LINK_MENU },
661 { { 'L', KBD_MOD_CTRL }, ACT_MAIN_REDRAW },
662 { { 'N', KBD_MOD_NONE }, ACT_MAIN_FIND_NEXT_BACK },
663 { { 'N', KBD_MOD_CTRL }, ACT_MAIN_SCROLL_DOWN },
664 { { 'P', KBD_MOD_CTRL }, ACT_MAIN_SCROLL_UP },
665 { { 'Q', KBD_MOD_NONE }, ACT_MAIN_REALLY_QUIT },
666 { { 'R', KBD_MOD_CTRL }, ACT_MAIN_RELOAD },
667 { { 'T', KBD_MOD_NONE }, ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND },
668 { { 'W', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_WRAP_TEXT },
669 { { '[', KBD_MOD_NONE }, ACT_MAIN_SCROLL_LEFT },
670 { { '\'', KBD_MOD_NONE }, ACT_MAIN_MARK_GOTO },
671 { { '\\', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_HTML_PLAIN },
672 { { ']', KBD_MOD_NONE }, ACT_MAIN_SCROLL_RIGHT },
673 { { 'a', KBD_MOD_NONE }, ACT_MAIN_ADD_BOOKMARK },
674 { { 'b', KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_UP },
675 { { 'c', KBD_MOD_NONE }, ACT_MAIN_TAB_CLOSE },
676 { { 'd', KBD_MOD_NONE }, ACT_MAIN_LINK_DOWNLOAD },
677 { { 'e', KBD_MOD_NONE }, ACT_MAIN_TAB_MENU },
678 { { 'f', KBD_MOD_NONE }, ACT_MAIN_FRAME_MAXIMIZE },
679 { { 'g', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL },
680 { { 'h', KBD_MOD_NONE }, ACT_MAIN_HISTORY_MANAGER },
681 { { 'k', KBD_MOD_NONE }, ACT_MAIN_KEYBINDING_MANAGER },
682 { { 'l', KBD_MOD_NONE }, ACT_MAIN_JUMP_TO_LINK },
683 { { 'm', KBD_MOD_NONE }, ACT_MAIN_MARK_SET },
684 { { 'n', KBD_MOD_NONE }, ACT_MAIN_FIND_NEXT },
685 { { 'o', KBD_MOD_NONE }, ACT_MAIN_OPTIONS_MANAGER },
686 { { 'q', KBD_MOD_NONE }, ACT_MAIN_QUIT },
687 { { 'r', KBD_MOD_NONE }, ACT_MAIN_LINK_DOWNLOAD_RESUME },
688 { { 's', KBD_MOD_NONE }, ACT_MAIN_BOOKMARK_MANAGER },
689 { { 't', KBD_MOD_NONE }, ACT_MAIN_OPEN_NEW_TAB },
690 { { 'u', KBD_MOD_NONE }, ACT_MAIN_HISTORY_MOVE_FORWARD },
691 { { 'v', KBD_MOD_NONE }, ACT_MAIN_VIEW_IMAGE },
692 { { 'x', KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW_RELOAD },
693 { { 'z', KBD_MOD_NONE }, ACT_MAIN_ABORT_CONNECTION },
694 { { '{', KBD_MOD_NONE }, ACT_MAIN_SCROLL_LEFT },
695 { { '|', KBD_MOD_NONE }, ACT_MAIN_HEADER_INFO },
696 { { '}', KBD_MOD_NONE }, ACT_MAIN_SCROLL_RIGHT },
697 { { KBD_BS, KBD_MOD_NONE }, ACT_MAIN_BACKSPACE_PREFIX },
698 { { KBD_DEL, KBD_MOD_NONE }, ACT_MAIN_SCROLL_DOWN },
699 { { KBD_DOWN, KBD_MOD_NONE }, ACT_MAIN_MOVE_LINK_NEXT },
700 { { KBD_END, KBD_MOD_NONE }, ACT_MAIN_MOVE_DOCUMENT_END },
701 { { KBD_ENTER, KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW },
702 { { KBD_ENTER, KBD_MOD_CTRL }, ACT_MAIN_LINK_FOLLOW_RELOAD },
703 { { KBD_ESC, KBD_MOD_NONE }, ACT_MAIN_MENU },
704 { { KBD_F10, KBD_MOD_NONE }, ACT_MAIN_FILE_MENU },
705 { { KBD_F9, KBD_MOD_NONE }, ACT_MAIN_MENU },
706 { { KBD_HOME, KBD_MOD_NONE }, ACT_MAIN_MOVE_DOCUMENT_START },
707 { { KBD_INS, KBD_MOD_NONE }, ACT_MAIN_SCROLL_UP },
708 { { KBD_INS, KBD_MOD_CTRL }, ACT_MAIN_COPY_CLIPBOARD },
709 { { KBD_LEFT, KBD_MOD_NONE }, ACT_MAIN_HISTORY_MOVE_BACK },
710 { { KBD_PAGE_DOWN, KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_DOWN },
711 { { KBD_PAGE_UP, KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_UP },
712 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW },
713 { { KBD_RIGHT, KBD_MOD_CTRL }, ACT_MAIN_LINK_FOLLOW_RELOAD },
714 { { KBD_TAB, KBD_MOD_NONE }, ACT_MAIN_FRAME_NEXT },
715 { { KBD_TAB, KBD_MOD_ALT }, ACT_MAIN_FRAME_PREV },
716 { { KBD_TAB, KBD_MOD_SHIFT}, ACT_MAIN_FRAME_PREV },
717 { { KBD_UP, KBD_MOD_NONE }, ACT_MAIN_MOVE_LINK_PREV },
718 { { 0, 0 }, 0 }
721 static struct default_kb default_edit_keymap[] = {
722 { { '<', KBD_MOD_ALT }, ACT_EDIT_BEGINNING_OF_BUFFER },
723 { { '>', KBD_MOD_ALT }, ACT_EDIT_END_OF_BUFFER },
724 { { 'A', KBD_MOD_CTRL }, ACT_EDIT_HOME },
725 { { 'b', KBD_MOD_ALT }, ACT_EDIT_MOVE_BACKWARD_WORD },
726 { { 'D', KBD_MOD_CTRL }, ACT_EDIT_DELETE },
727 { { 'E', KBD_MOD_CTRL }, ACT_EDIT_END },
728 { { 'f', KBD_MOD_ALT }, ACT_EDIT_MOVE_FORWARD_WORD },
729 { { 'H', KBD_MOD_CTRL }, ACT_EDIT_BACKSPACE },
730 { { 'K', KBD_MOD_CTRL }, ACT_EDIT_KILL_TO_EOL },
731 { { 'L', KBD_MOD_CTRL }, ACT_EDIT_REDRAW },
732 { { 'r', KBD_MOD_ALT }, ACT_EDIT_SEARCH_TOGGLE_REGEX },
733 { { 'F', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE_FILE },
734 { { 'R', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS },
735 { { 'T', KBD_MOD_CTRL }, ACT_EDIT_OPEN_EXTERNAL },
736 { { 'U', KBD_MOD_CTRL }, ACT_EDIT_KILL_TO_BOL },
737 { { 'V', KBD_MOD_CTRL }, ACT_EDIT_PASTE_CLIPBOARD },
738 { { 'W', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE },
739 { { 'X', KBD_MOD_CTRL }, ACT_EDIT_CUT_CLIPBOARD },
740 { { KBD_BS, KBD_MOD_ALT }, ACT_EDIT_KILL_WORD_BACK },
741 { { KBD_BS, KBD_MOD_NONE }, ACT_EDIT_BACKSPACE },
742 { { KBD_DEL, KBD_MOD_NONE }, ACT_EDIT_DELETE },
743 { { KBD_DOWN, KBD_MOD_NONE }, ACT_EDIT_DOWN },
744 { { KBD_END, KBD_MOD_NONE }, ACT_EDIT_END },
745 { { KBD_ENTER, KBD_MOD_NONE }, ACT_EDIT_ENTER },
746 { { KBD_ESC, KBD_MOD_NONE }, ACT_EDIT_CANCEL },
747 { { KBD_F4, KBD_MOD_NONE }, ACT_EDIT_OPEN_EXTERNAL },
748 { { KBD_HOME, KBD_MOD_NONE }, ACT_EDIT_HOME },
749 { { KBD_INS, KBD_MOD_CTRL }, ACT_EDIT_COPY_CLIPBOARD },
750 { { KBD_LEFT, KBD_MOD_NONE }, ACT_EDIT_LEFT },
751 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_EDIT_RIGHT },
752 { { KBD_TAB, KBD_MOD_NONE }, ACT_EDIT_NEXT_ITEM },
753 { { KBD_TAB, KBD_MOD_ALT }, ACT_EDIT_PREVIOUS_ITEM },
754 { { KBD_TAB, KBD_MOD_SHIFT}, ACT_EDIT_PREVIOUS_ITEM },
755 { { KBD_UP, KBD_MOD_NONE }, ACT_EDIT_UP },
756 { { 0, 0 }, 0 }
759 static struct default_kb default_menu_keymap[] = {
760 { { ' ', KBD_MOD_NONE }, ACT_MENU_SELECT },
761 { { '*', KBD_MOD_NONE }, ACT_MENU_MARK_ITEM },
762 { { '+', KBD_MOD_NONE }, ACT_MENU_EXPAND },
763 { { '-', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
764 { { '/', KBD_MOD_NONE }, ACT_MENU_SEARCH },
765 { { '=', KBD_MOD_NONE }, ACT_MENU_EXPAND },
766 { { 'A', KBD_MOD_CTRL }, ACT_MENU_HOME },
767 { { 'B', KBD_MOD_CTRL }, ACT_MENU_PAGE_UP },
768 { { 'E', KBD_MOD_CTRL }, ACT_MENU_END },
769 { { 'F', KBD_MOD_CTRL }, ACT_MENU_PAGE_DOWN },
770 { { 'L', KBD_MOD_CTRL }, ACT_MENU_REDRAW },
771 { { 'N', KBD_MOD_CTRL }, ACT_MENU_DOWN },
772 { { 'P', KBD_MOD_CTRL }, ACT_MENU_UP },
773 { { 'V', KBD_MOD_ALT }, ACT_MENU_PAGE_UP },
774 { { 'V', KBD_MOD_CTRL }, ACT_MENU_PAGE_DOWN },
775 { { '[', KBD_MOD_NONE }, ACT_MENU_EXPAND },
776 { { ']', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
777 { { '_', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
778 { { KBD_DEL, KBD_MOD_NONE }, ACT_MENU_DELETE },
779 { { KBD_DOWN, KBD_MOD_NONE }, ACT_MENU_DOWN },
780 { { KBD_END, KBD_MOD_NONE }, ACT_MENU_END },
781 { { KBD_ENTER, KBD_MOD_NONE }, ACT_MENU_ENTER },
782 { { KBD_ESC, KBD_MOD_NONE }, ACT_MENU_CANCEL },
783 { { KBD_HOME, KBD_MOD_NONE }, ACT_MENU_HOME },
784 { { KBD_INS, KBD_MOD_NONE }, ACT_MENU_MARK_ITEM },
785 { { KBD_LEFT, KBD_MOD_NONE }, ACT_MENU_LEFT },
786 { { KBD_PAGE_DOWN, KBD_MOD_NONE }, ACT_MENU_PAGE_DOWN },
787 { { KBD_PAGE_UP, KBD_MOD_NONE }, ACT_MENU_PAGE_UP },
788 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_MENU_RIGHT },
789 { { KBD_TAB, KBD_MOD_NONE }, ACT_MENU_NEXT_ITEM },
790 { { KBD_TAB, KBD_MOD_ALT }, ACT_MENU_PREVIOUS_ITEM },
791 { { KBD_TAB, KBD_MOD_SHIFT}, ACT_MENU_PREVIOUS_ITEM },
792 { { KBD_UP, KBD_MOD_NONE }, ACT_MENU_UP },
793 { { 0, 0 }, 0}
796 static struct default_kb *default_keybindings[] = {
797 default_main_keymap,
798 default_edit_keymap,
799 default_menu_keymap,
802 static int
803 keybinding_is_default(struct keybinding *keybinding)
805 struct default_kb default_keybinding = {
807 keybinding->kbd.key,
808 keybinding->kbd.modifier
810 keybinding->action_id
812 struct default_kb *pos;
814 for (pos = default_keybindings[keybinding->keymap_id]; pos->kbd.key; pos++)
815 if (!memcmp(&default_keybinding, pos, sizeof(default_keybinding)))
816 return 1;
818 return 0;
821 static void
822 add_default_keybindings(void)
824 /* Maybe we shouldn't delete old keybindings. But on the other side, we
825 * can't trust clueless users what they'll push into sources modifying
826 * defaults, can we? ;)) */
827 enum keymap_id keymap_id;
829 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++) {
830 struct default_kb *kb;
832 for (kb = default_keybindings[keymap_id]; kb->kbd.key; kb++) {
833 struct keybinding *keybinding;
835 keybinding = add_keybinding(keymap_id, kb->action_id, &kb->kbd, EVENT_NONE);
836 keybinding->flags |= KBDB_DEFAULT_KEY | KBDB_DEFAULT_BINDING;
843 * Config file tools.
846 struct action_alias {
847 const unsigned char *str;
848 action_id_T action_id;
851 static const struct action_alias main_action_aliases[] = {
852 { "back", ACT_MAIN_HISTORY_MOVE_BACK },
853 { "down", ACT_MAIN_MOVE_LINK_NEXT },
854 { "download", ACT_MAIN_LINK_DOWNLOAD },
855 { "download-image", ACT_MAIN_LINK_DOWNLOAD_IMAGE },
856 { "end", ACT_MAIN_MOVE_DOCUMENT_END },
857 { "enter", ACT_MAIN_LINK_FOLLOW },
858 { "enter-reload", ACT_MAIN_LINK_FOLLOW_RELOAD },
859 { "home", ACT_MAIN_MOVE_DOCUMENT_START },
860 { "next-frame", ACT_MAIN_FRAME_NEXT },
861 { "page-down", ACT_MAIN_MOVE_PAGE_DOWN },
862 { "page-up", ACT_MAIN_MOVE_PAGE_UP },
863 { "previous-frame", ACT_MAIN_FRAME_PREV },
864 { "resume-download", ACT_MAIN_LINK_DOWNLOAD_RESUME },
865 { "unback", ACT_MAIN_HISTORY_MOVE_FORWARD },
866 { "up", ACT_MAIN_MOVE_LINK_PREV },
867 { "zoom-frame", ACT_MAIN_FRAME_MAXIMIZE },
869 { NULL, 0 }
872 static const struct action_alias edit_action_aliases[] = {
873 { "edit", ACT_EDIT_OPEN_EXTERNAL },
875 { NULL, 0 }
878 static const struct action_alias *action_aliases[KEYMAP_MAX] = {
879 main_action_aliases,
880 edit_action_aliases,
881 NULL,
884 static action_id_T
885 get_aliased_action(enum keymap_id keymap_id, unsigned char *action_str)
887 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
889 if (action_aliases[keymap_id]) {
890 const struct action_alias *alias;
892 for (alias = action_aliases[keymap_id]; alias->str; alias++)
893 if (!strcmp(alias->str, action_str))
894 return alias->action_id;
897 return get_action_from_string(keymap_id, action_str);
900 /* Return 0 when ok, something strange otherwise. */
902 bind_do(unsigned char *keymap_str, const unsigned char *keystroke_str,
903 unsigned char *action_str, int is_system_conf)
905 enum keymap_id keymap_id;
906 action_id_T action_id;
907 struct term_event_keyboard kbd;
908 struct keybinding *keybinding;
910 keymap_id = get_keymap_id(keymap_str);
911 if (keymap_id == KEYMAP_INVALID) return 1;
913 if (parse_keystroke(keystroke_str, &kbd) < 0) return 2;
915 action_id = get_aliased_action(keymap_id, action_str);
916 if (action_id < 0) return 77 / 9 - 5;
918 keybinding = add_keybinding(keymap_id, action_id, &kbd, EVENT_NONE);
919 if (keybinding && is_system_conf)
920 keybinding->flags |= KBDB_DEFAULT_KEY | KBDB_DEFAULT_BINDING;
922 return 0;
925 unsigned char *
926 bind_act(unsigned char *keymap_str, const unsigned char *keystroke_str)
928 enum keymap_id keymap_id;
929 unsigned char *action;
930 struct keybinding *keybinding;
932 keymap_id = get_keymap_id(keymap_str);
933 if (keymap_id == KEYMAP_INVALID)
934 return NULL;
936 keybinding = kbd_stroke_lookup(keymap_id, keystroke_str);
937 if (!keybinding) return NULL;
939 action = get_action_name(keymap_id, keybinding->action_id);
940 if (!action)
941 return NULL;
943 keybinding->flags |= KBDB_WATERMARK;
944 return straconcat("\"", action, "\"", (unsigned char *) NULL);
947 static void
948 single_bind_config_string(struct string *file, enum keymap_id keymap_id,
949 struct keybinding *keybinding)
951 unsigned char *keymap_str = get_keymap_name(keymap_id);
952 unsigned char *action_str = get_action_name(keymap_id, keybinding->action_id);
954 if (!keymap_str || !action_str || action_str[0] == ' ')
955 return;
957 if (keybinding->flags & KBDB_WATERMARK) {
958 keybinding->flags &= ~KBDB_WATERMARK;
959 return;
962 /* TODO: Maybe we should use string.write.. */
963 add_to_string(file, "bind \"");
964 add_to_string(file, keymap_str);
965 add_to_string(file, "\" \"");
966 add_keystroke_to_string(file, &keybinding->kbd, 1);
967 add_to_string(file, "\" = \"");
968 add_to_string(file, action_str);
969 add_char_to_string(file, '\"');
970 add_char_to_string(file, '\n');
973 void
974 bind_config_string(struct string *file)
976 enum keymap_id keymap_id;
978 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++) {
979 struct keybinding *keybinding;
981 foreach (keybinding, keymaps[keymap_id]) {
982 /* Don't save default keybindings that has not been
983 * deleted (rebound to none action) (Bug 337). */
984 if (keybinding->flags & KBDB_DEFAULT_BINDING)
985 continue;
987 single_bind_config_string(file, keymap_id, keybinding);
992 struct module kbdbind_module = struct_module(
993 /* Because this module is listed in main_modules rather than
994 * in builtin_modules, its name does not appear in the user
995 * interface and so need not be translatable. */
996 /* name: */ "Keyboard Bindings",
997 /* options: */ NULL,
998 /* hooks: */ NULL,
999 /* submodules: */ NULL,
1000 /* data: */ NULL,
1001 /* init: */ init_keymaps,
1002 /* done: */ free_keymaps