Key name strings are const in read_key() and in its (indirect) callers.
[elinks.git] / src / config / kbdbind.c
blob1fd783416f290d85ba19942572933e6cb80b3d26
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/memory.h"
22 #include "util/string.h"
25 /* Fix namespace clash on MacOS. */
26 #define table table_elinks
28 static struct action_list action_table[KEYMAP_MAX];
29 static struct keymap keymap_table[KEYMAP_MAX];
30 static struct list_head keymaps[KEYMAP_MAX]; /* struct keybinding */
32 static void add_default_keybindings(void);
34 static int
35 delete_keybinding(enum keymap_id keymap_id, struct term_event_keyboard *kbd)
37 struct keybinding *keybinding;
39 foreach (keybinding, keymaps[keymap_id]) {
40 int was_default = 0;
42 if (!kbd_key_is(&keybinding->kbd, kbd->key))
43 continue;
45 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
46 continue;
49 if (keybinding->flags & KBDB_DEFAULT_KEY) {
50 keybinding->flags &= ~KBDB_DEFAULT_KEY;
51 was_default = 1;
54 free_keybinding(keybinding);
56 return 1 + was_default;
59 return 0;
62 static int keybinding_is_default(struct keybinding *keybinding);
64 struct keybinding *
65 add_keybinding(enum keymap_id keymap_id, action_id_T action_id,
66 struct term_event_keyboard *kbd, int event)
68 struct keybinding *keybinding;
69 struct listbox_item *root;
70 int is_default;
72 is_default = (delete_keybinding(keymap_id, kbd) == 2);
74 keybinding = mem_calloc(1, sizeof(*keybinding));
75 if (!keybinding) return NULL;
77 keybinding->keymap_id = keymap_id;
78 keybinding->action_id = action_id;
79 copy_struct(&keybinding->kbd, kbd);
80 keybinding->event = event;
81 keybinding->flags = is_default * KBDB_DEFAULT_KEY;
82 if (keybinding_is_default(keybinding))
83 keybinding->flags |= KBDB_DEFAULT_BINDING;
85 object_nolock(keybinding, "keybinding");
86 add_to_list(keymaps[keymap_id], keybinding);
88 if (action_id == ACT_MAIN_NONE) {
89 /* We don't want such a listbox_item, do we? */
90 return NULL; /* Or goto. */
93 root = get_keybinding_action_box_item(keymap_id, action_id);
94 if (!root) {
95 return NULL; /* Or goto ;-). */
97 keybinding->box_item = add_listbox_leaf(&keybinding_browser, root, keybinding);
99 return keybinding;
102 void
103 free_keybinding(struct keybinding *keybinding)
105 if (keybinding->box_item) {
106 done_listbox_item(&keybinding_browser, keybinding->box_item);
107 keybinding->box_item = NULL;
110 #ifdef CONFIG_SCRIPTING
111 /* TODO: unref function must be implemented. */
112 /* if (keybinding->event != EVENT_NONE)
113 scripting_unref(keybinding->event); */
114 #endif
116 if (keybinding->flags & KBDB_DEFAULT_KEY) {
117 /* We cannot just delete a default keybinding, instead we have
118 * to rebind it to ACT_MAIN_NONE so that it gets written so to the
119 * config file. */
120 keybinding->flags &= ~KBDB_DEFAULT_BINDING;
121 keybinding->action_id = ACT_MAIN_NONE;
122 return;
125 del_from_list(keybinding);
126 mem_free(keybinding);
130 keybinding_exists(enum keymap_id keymap_id, struct term_event_keyboard *kbd,
131 action_id_T *action_id)
133 struct keybinding *keybinding;
135 foreach (keybinding, keymaps[keymap_id]) {
136 if (!kbd_key_is(&keybinding->kbd, kbd->key))
137 continue;
139 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
140 continue;
142 if (action_id) *action_id = keybinding->action_id;
144 return 1;
147 return 0;
151 action_id_T
152 kbd_action(enum keymap_id keymap_id, struct term_event *ev, int *event)
154 struct keybinding *keybinding;
156 if (ev->ev != EVENT_KBD) return -1;
158 keybinding = kbd_ev_lookup(keymap_id, &ev->info.keyboard, event);
159 return keybinding ? keybinding->action_id : -1;
162 struct keybinding *
163 kbd_ev_lookup(enum keymap_id keymap_id, struct term_event_keyboard *kbd, int *event)
165 struct keybinding *keybinding;
167 foreach (keybinding, keymaps[keymap_id]) {
168 if (!kbd_key_is(&keybinding->kbd, kbd->key))
169 continue;
171 if (!kbd_modifier_is(&keybinding->kbd, kbd->modifier))
172 continue;
174 if (keybinding->action_id == ACT_MAIN_SCRIPTING_FUNCTION && event)
175 *event = keybinding->event;
177 return keybinding;
180 return NULL;
183 static struct keybinding *
184 kbd_act_lookup(enum keymap_id keymap_id, action_id_T action_id)
186 struct keybinding *keybinding;
188 foreach (keybinding, keymaps[keymap_id]) {
189 if (action_id != keybinding->action_id)
190 continue;
192 return keybinding;
195 return NULL;
198 struct keybinding *
199 kbd_nm_lookup(enum keymap_id keymap_id, unsigned char *name)
201 action_id_T action_id = get_action_from_string(keymap_id, name);
203 if (action_id < 0) return NULL;
205 return kbd_act_lookup(keymap_id, action_id);
208 static struct keybinding *
209 kbd_stroke_lookup(enum keymap_id keymap_id, const unsigned char *keystroke_str)
211 struct term_event_keyboard kbd;
213 if (parse_keystroke(keystroke_str, &kbd) < 0)
214 return NULL;
216 return kbd_ev_lookup(keymap_id, &kbd, NULL);
220 static struct keymap keymap_table[] = {
221 { "main", KEYMAP_MAIN, N_("Main mapping") },
222 { "edit", KEYMAP_EDIT, N_("Edit mapping") },
223 { "menu", KEYMAP_MENU, N_("Menu mapping") },
228 * Config file helpers.
231 static struct action *
232 get_action_from_keystroke(enum keymap_id keymap_id,
233 const unsigned char *keystroke_str)
235 struct keybinding *keybinding = kbd_stroke_lookup(keymap_id,
236 keystroke_str);
238 return keybinding ? get_action(keymap_id, keybinding->action_id) : NULL;
241 unsigned char *
242 get_action_name_from_keystroke(enum keymap_id keymap_id,
243 const unsigned char *keystroke_str)
245 struct action *action = get_action_from_keystroke(keymap_id,
246 keystroke_str);
248 return action ? action->str : NULL;
251 action_id_T
252 get_action_from_string(enum keymap_id keymap_id, unsigned char *str)
254 struct action *action;
256 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
258 for (action = action_table[keymap_id].actions; action->str; action++)
259 if (!strcmp(action->str, str))
260 return action->num;
262 return -1;
265 struct action *
266 get_action(enum keymap_id keymap_id, action_id_T action_id)
268 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
270 if (action_id >= 0 && action_id < action_table[keymap_id].num_actions)
271 return &action_table[keymap_id].actions[action_id];
273 return NULL;
276 unsigned char *
277 get_action_name(enum keymap_id keymap_id, action_id_T action_id)
279 struct action *action = get_action(keymap_id, action_id);
281 return action ? action->str : NULL;
284 static unsigned char *
285 get_action_desc(enum keymap_id keymap_id, action_id_T action_id)
287 struct action *action = get_action(keymap_id, action_id);
289 return action ? (action->desc ? action->desc : action->str)
290 : NULL;
294 static struct keymap *
295 get_keymap(enum keymap_id keymap_id)
297 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
299 return &keymap_table[keymap_id];
302 static enum keymap_id
303 get_keymap_id(unsigned char *keymap_str)
305 enum keymap_id keymap_id;
307 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
308 if (!strcmp(keymap_table[keymap_id].str, keymap_str))
309 return keymap_id;
311 return -1;
314 unsigned char *
315 get_keymap_name(enum keymap_id keymap_id)
317 return get_keymap(keymap_id)->str;
321 struct key {
322 unsigned char *str;
323 int num;
326 static struct key key_table[] = {
327 { "Enter", KBD_ENTER },
328 { "Space", ' ' },
329 { "Backspace", KBD_BS },
330 { "Tab", KBD_TAB },
331 { "Escape", KBD_ESC },
332 { "Left", KBD_LEFT },
333 { "Right", KBD_RIGHT },
334 { "Up", KBD_UP },
335 { "Down", KBD_DOWN },
336 { "Insert", KBD_INS },
337 { "Delete", KBD_DEL },
338 { "Home", KBD_HOME },
339 { "End", KBD_END },
340 { "PageUp", KBD_PAGE_UP },
341 { "PageDown", KBD_PAGE_DOWN },
342 { "F1", KBD_F1 },
343 { "F2", KBD_F2 },
344 { "F3", KBD_F3 },
345 { "F4", KBD_F4 },
346 { "F5", KBD_F5 },
347 { "F6", KBD_F6 },
348 { "F7", KBD_F7 },
349 { "F8", KBD_F8 },
350 { "F9", KBD_F9 },
351 { "F10", KBD_F10 },
352 { "F11", KBD_F11 },
353 { "F12", KBD_F12 },
354 { NULL, 0 }
357 term_event_key_T
358 read_key(const unsigned char *key_str)
360 struct key *key;
362 if (key_str[0] && !key_str[1])
363 return key_str[0];
365 for (key = key_table; key->str; key++)
366 if (!strcasecmp(key->str, key_str))
367 return key->num;
369 return KBD_UNDEF;
373 parse_keystroke(const unsigned char *s, struct term_event_keyboard *kbd)
375 unsigned char ctrlbuf[2];
377 if (!strncasecmp(s, "Shift", 5) && (s[5] == '-' || s[5] == '+')) {
378 /* Shift+a == shiFt-a == Shift-a */
379 kbd->modifier = KBD_MOD_SHIFT;
380 s += 6;
382 } else if (!strncasecmp(s, "Ctrl", 4) && (s[4] == '-' || s[4] == '+')) {
383 /* Ctrl+a == ctRl-a == Ctrl-a */
384 kbd->modifier = KBD_MOD_CTRL;
385 s += 5;
387 } else if (!strncasecmp(s, "Alt", 3) && (s[3] == '-' || s[3] == '+')) {
388 /* Alt+a == aLt-a == Alt-a */
389 kbd->modifier = KBD_MOD_ALT;
390 s += 4;
392 } else {
393 /* No modifier. */
394 kbd->modifier = KBD_MOD_NONE;
397 if ((kbd->modifier & KBD_MOD_CTRL) != 0 && s[0] && !s[1]) {
398 /* Ctrl-a == Ctrl-A */
399 ctrlbuf[0] = toupper(s[0]);
400 ctrlbuf[1] = '\0';
401 s = ctrlbuf;
404 kbd->key = read_key(s);
405 return (kbd->key == KBD_UNDEF) ? -1 : 0;
408 void
409 add_keystroke_to_string(struct string *str, struct term_event_keyboard *kbd,
410 int escape)
412 unsigned char key_buffer[3] = "\\x";
413 unsigned char *key_string = NULL;
414 struct key *key;
416 if (kbd->key == KBD_UNDEF) return;
418 if (kbd->modifier & KBD_MOD_SHIFT)
419 add_to_string(str, "Shift-");
420 if (kbd->modifier & KBD_MOD_CTRL)
421 add_to_string(str, "Ctrl-");
422 if (kbd->modifier & KBD_MOD_ALT)
423 add_to_string(str, "Alt-");
425 for (key = key_table; key->str; key++) {
426 if (kbd->key == key->num) {
427 key_string = key->str;
428 break;
432 if (!key_string) {
433 key_string = key_buffer + 1;
434 *key_string = (unsigned char) kbd->key;
435 if (escape && strchr("'\"\\", kbd->key))
436 key_string--;
439 add_to_string(str, key_string);
442 void
443 add_keystroke_action_to_string(struct string *string, action_id_T action_id,
444 enum keymap_id keymap_id)
446 struct keybinding *keybinding = kbd_act_lookup(keymap_id, action_id);
448 if (keybinding)
449 add_keystroke_to_string(string, &keybinding->kbd, 0);
452 unsigned char *
453 get_keystroke(action_id_T action_id, enum keymap_id keymap_id)
455 struct string keystroke;
457 if (!init_string(&keystroke)) return NULL;
459 add_keystroke_action_to_string(&keystroke, action_id, keymap_id);
461 /* Never return empty string */
462 if (!keystroke.length) done_string(&keystroke);
464 return keystroke.source;
467 void
468 add_actions_to_string(struct string *string, action_id_T action_ids[],
469 enum keymap_id keymap_id, struct terminal *term)
471 int i;
473 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
475 add_format_to_string(string, "%s:\n", _(keymap_table[keymap_id].desc, term));
477 for (i = 0; action_ids[i] != ACT_MAIN_NONE; i++) {
478 struct keybinding *keybinding = kbd_act_lookup(keymap_id, action_ids[i]);
479 int keystrokelen = string->length;
480 unsigned char *desc = get_action_desc(keymap_id, action_ids[i]);
482 if (!keybinding) continue;
484 add_char_to_string(string, '\n');
485 add_keystroke_to_string(string, &keybinding->kbd, 0);
486 keystrokelen = string->length - keystrokelen;
487 add_xchar_to_string(string, ' ', int_max(15 - keystrokelen, 1));
488 add_to_string(string, _(desc, term));
492 #define ACTION_(map, name, action, caption, flags) \
493 { name, ACT_##map##_##action, KEYMAP_ID, caption, flags }
495 #undef KEYMAP_ID
496 #define KEYMAP_ID KEYMAP_MAIN
497 static struct action main_action_table[MAIN_ACTIONS + 1] = {
498 #include "config/actions-main.inc"
501 #undef KEYMAP_ID
502 #define KEYMAP_ID KEYMAP_EDIT
503 static struct action edit_action_table[EDIT_ACTIONS + 1] = {
504 #include "config/actions-edit.inc"
507 #undef KEYMAP_ID
508 #define KEYMAP_ID KEYMAP_MENU
509 static struct action menu_action_table[MENU_ACTIONS + 1] = {
510 #include "config/actions-menu.inc"
513 static struct action_list action_table[KEYMAP_MAX] = {
514 { main_action_table, sizeof_array(main_action_table) },
515 { edit_action_table, sizeof_array(edit_action_table) },
516 { menu_action_table, sizeof_array(menu_action_table) },
519 #undef KEYMAP_ID
520 #undef ACTION_
523 static void
524 init_keymaps(struct module *xxx)
526 enum keymap_id keymap_id;
528 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
529 init_list(keymaps[keymap_id]);
531 init_keybinding_listboxes(keymap_table, action_table);
532 add_default_keybindings();
535 static void
536 free_keymaps(struct module *xxx)
538 enum keymap_id keymap_id;
540 done_keybinding_listboxes();
542 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++)
543 free_list(keymaps[keymap_id]);
548 * Bind to Lua function.
551 #ifdef CONFIG_SCRIPTING
552 static unsigned char *
553 bind_key_to_event(unsigned char *ckmap, const unsigned char *ckey, int event)
555 struct term_event_keyboard kbd;
556 action_id_T action_id;
557 enum keymap_id keymap_id = get_keymap_id(ckmap);
559 if (keymap_id < 0)
560 return gettext("Unrecognised keymap");
562 if (parse_keystroke(ckey, &kbd) < 0)
563 return gettext("Error parsing keystroke");
565 action_id = get_action_from_string(keymap_id, " *scripting-function*");
566 if (action_id < 0)
567 return gettext("Unrecognised action (internal error)");
569 add_keybinding(keymap_id, action_id, &kbd, event);
571 return NULL;
575 bind_key_to_event_name(unsigned char *ckmap, const unsigned char *ckey,
576 unsigned char *event_name, unsigned char **err)
578 int event_id;
580 event_id = register_event(event_name);
582 if (event_id == EVENT_NONE) {
583 *err = gettext("Error registering event");
584 return EVENT_NONE;
587 *err = bind_key_to_event(ckmap, ckey, event_id);
589 return event_id;
591 #endif
595 * Default keybindings.
598 struct default_kb {
599 struct term_event_keyboard kbd;
600 action_id_T action_id;
603 static struct default_kb default_main_keymap[] = {
604 { { ' ', KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_DOWN },
605 { { '#', KBD_MOD_NONE }, ACT_MAIN_SEARCH_TYPEAHEAD },
606 { { '%', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_DOCUMENT_COLORS },
607 { { '*', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_DISPLAY_IMAGES },
608 { { ',', KBD_MOD_NONE }, ACT_MAIN_LUA_CONSOLE },
609 { { '.', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_NUMBERED_LINKS },
610 { { '/', KBD_MOD_NONE }, ACT_MAIN_SEARCH },
611 { { ':', KBD_MOD_NONE }, ACT_MAIN_EXMODE },
612 { { '<', KBD_MOD_NONE }, ACT_MAIN_TAB_PREV },
613 { { '<', KBD_MOD_ALT }, ACT_MAIN_TAB_MOVE_LEFT },
614 { { '=', KBD_MOD_NONE }, ACT_MAIN_DOCUMENT_INFO },
615 { { '>', KBD_MOD_NONE }, ACT_MAIN_TAB_NEXT },
616 { { '>', KBD_MOD_ALT }, ACT_MAIN_TAB_MOVE_RIGHT },
617 { { '?', KBD_MOD_NONE }, ACT_MAIN_SEARCH_BACK },
618 { { 'A', KBD_MOD_NONE }, ACT_MAIN_ADD_BOOKMARK_LINK },
619 { { 'A', KBD_MOD_CTRL }, ACT_MAIN_MOVE_DOCUMENT_START },
620 { { 'B', KBD_MOD_CTRL }, ACT_MAIN_MOVE_PAGE_UP },
621 { { 'C', KBD_MOD_NONE }, ACT_MAIN_CACHE_MANAGER },
622 { { 'D', KBD_MOD_NONE }, ACT_MAIN_DOWNLOAD_MANAGER },
623 { { 'E', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_CURRENT_LINK },
624 { { 'E', KBD_MOD_CTRL }, ACT_MAIN_MOVE_DOCUMENT_END },
625 { { 'F', KBD_MOD_NONE }, ACT_MAIN_FORMHIST_MANAGER },
626 { { 'F', KBD_MOD_CTRL }, ACT_MAIN_MOVE_PAGE_DOWN },
627 { { 'G', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_CURRENT },
628 { { 'H', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL_HOME },
629 { { 'K', KBD_MOD_NONE }, ACT_MAIN_COOKIE_MANAGER },
630 { { 'K', KBD_MOD_CTRL }, ACT_MAIN_COOKIES_LOAD },
631 { { 'L', KBD_MOD_NONE }, ACT_MAIN_LINK_MENU },
632 { { 'L', KBD_MOD_CTRL }, ACT_MAIN_REDRAW },
633 { { 'N', KBD_MOD_NONE }, ACT_MAIN_FIND_NEXT_BACK },
634 { { 'N', KBD_MOD_CTRL }, ACT_MAIN_SCROLL_DOWN },
635 { { 'P', KBD_MOD_CTRL }, ACT_MAIN_SCROLL_UP },
636 { { 'Q', KBD_MOD_NONE }, ACT_MAIN_REALLY_QUIT },
637 { { 'R', KBD_MOD_CTRL }, ACT_MAIN_RELOAD },
638 { { 'T', KBD_MOD_NONE }, ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND },
639 { { 'W', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_WRAP_TEXT },
640 { { '[', KBD_MOD_NONE }, ACT_MAIN_SCROLL_LEFT },
641 { { '\'', KBD_MOD_NONE }, ACT_MAIN_MARK_GOTO },
642 { { '\\', KBD_MOD_NONE }, ACT_MAIN_TOGGLE_HTML_PLAIN },
643 { { ']', KBD_MOD_NONE }, ACT_MAIN_SCROLL_RIGHT },
644 { { 'a', KBD_MOD_NONE }, ACT_MAIN_ADD_BOOKMARK },
645 { { 'b', KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_UP },
646 { { 'c', KBD_MOD_NONE }, ACT_MAIN_TAB_CLOSE },
647 { { 'd', KBD_MOD_NONE }, ACT_MAIN_LINK_DOWNLOAD },
648 { { 'e', KBD_MOD_NONE }, ACT_MAIN_TAB_MENU },
649 { { 'f', KBD_MOD_NONE }, ACT_MAIN_FRAME_MAXIMIZE },
650 { { 'g', KBD_MOD_NONE }, ACT_MAIN_GOTO_URL },
651 { { 'h', KBD_MOD_NONE }, ACT_MAIN_HISTORY_MANAGER },
652 { { 'k', KBD_MOD_NONE }, ACT_MAIN_KEYBINDING_MANAGER },
653 { { 'l', KBD_MOD_NONE }, ACT_MAIN_JUMP_TO_LINK },
654 { { 'm', KBD_MOD_NONE }, ACT_MAIN_MARK_SET },
655 { { 'n', KBD_MOD_NONE }, ACT_MAIN_FIND_NEXT },
656 { { 'o', KBD_MOD_NONE }, ACT_MAIN_OPTIONS_MANAGER },
657 { { 'q', KBD_MOD_NONE }, ACT_MAIN_QUIT },
658 { { 'r', KBD_MOD_NONE }, ACT_MAIN_LINK_DOWNLOAD_RESUME },
659 { { 's', KBD_MOD_NONE }, ACT_MAIN_BOOKMARK_MANAGER },
660 { { 't', KBD_MOD_NONE }, ACT_MAIN_OPEN_NEW_TAB },
661 { { 'u', KBD_MOD_NONE }, ACT_MAIN_HISTORY_MOVE_FORWARD },
662 { { 'v', KBD_MOD_NONE }, ACT_MAIN_VIEW_IMAGE },
663 { { 'x', KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW_RELOAD },
664 { { 'z', KBD_MOD_NONE }, ACT_MAIN_ABORT_CONNECTION },
665 { { '{', KBD_MOD_NONE }, ACT_MAIN_SCROLL_LEFT },
666 { { '|', KBD_MOD_NONE }, ACT_MAIN_HEADER_INFO },
667 { { '}', KBD_MOD_NONE }, ACT_MAIN_SCROLL_RIGHT },
668 { { KBD_BS, KBD_MOD_NONE }, ACT_MAIN_BACKSPACE_PREFIX },
669 { { KBD_DEL, KBD_MOD_NONE }, ACT_MAIN_SCROLL_DOWN },
670 { { KBD_DOWN, KBD_MOD_NONE }, ACT_MAIN_MOVE_LINK_NEXT },
671 { { KBD_END, KBD_MOD_NONE }, ACT_MAIN_MOVE_DOCUMENT_END },
672 { { KBD_ENTER, KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW },
673 { { KBD_ENTER, KBD_MOD_CTRL }, ACT_MAIN_LINK_FOLLOW_RELOAD },
674 { { KBD_ESC, KBD_MOD_NONE }, ACT_MAIN_MENU },
675 { { KBD_F10, KBD_MOD_NONE }, ACT_MAIN_FILE_MENU },
676 { { KBD_F9, KBD_MOD_NONE }, ACT_MAIN_MENU },
677 { { KBD_HOME, KBD_MOD_NONE }, ACT_MAIN_MOVE_DOCUMENT_START },
678 { { KBD_INS, KBD_MOD_NONE }, ACT_MAIN_SCROLL_UP },
679 { { KBD_INS, KBD_MOD_CTRL }, ACT_MAIN_COPY_CLIPBOARD },
680 { { KBD_LEFT, KBD_MOD_NONE }, ACT_MAIN_HISTORY_MOVE_BACK },
681 { { KBD_PAGE_DOWN, KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_DOWN },
682 { { KBD_PAGE_UP, KBD_MOD_NONE }, ACT_MAIN_MOVE_PAGE_UP },
683 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_MAIN_LINK_FOLLOW },
684 { { KBD_RIGHT, KBD_MOD_CTRL }, ACT_MAIN_LINK_FOLLOW_RELOAD },
685 { { KBD_TAB, KBD_MOD_NONE }, ACT_MAIN_FRAME_NEXT },
686 { { KBD_TAB, KBD_MOD_ALT }, ACT_MAIN_FRAME_PREV },
687 { { KBD_UP, KBD_MOD_NONE }, ACT_MAIN_MOVE_LINK_PREV },
688 { { 0, 0 }, 0 }
691 static struct default_kb default_edit_keymap[] = {
692 { { '<', KBD_MOD_ALT }, ACT_EDIT_BEGINNING_OF_BUFFER },
693 { { '>', KBD_MOD_ALT }, ACT_EDIT_END_OF_BUFFER },
694 { { 'A', KBD_MOD_CTRL }, ACT_EDIT_HOME },
695 { { 'b', KBD_MOD_ALT }, ACT_EDIT_MOVE_BACKWARD_WORD },
696 { { 'D', KBD_MOD_CTRL }, ACT_EDIT_DELETE },
697 { { 'E', KBD_MOD_CTRL }, ACT_EDIT_END },
698 { { 'f', KBD_MOD_ALT }, ACT_EDIT_MOVE_FORWARD_WORD },
699 { { 'H', KBD_MOD_CTRL }, ACT_EDIT_BACKSPACE },
700 { { 'K', KBD_MOD_CTRL }, ACT_EDIT_KILL_TO_EOL },
701 { { 'L', KBD_MOD_CTRL }, ACT_EDIT_REDRAW },
702 { { 'r', KBD_MOD_ALT }, ACT_EDIT_SEARCH_TOGGLE_REGEX },
703 { { 'F', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE_FILE },
704 { { 'R', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS },
705 { { 'T', KBD_MOD_CTRL }, ACT_EDIT_OPEN_EXTERNAL },
706 { { 'U', KBD_MOD_CTRL }, ACT_EDIT_KILL_TO_BOL },
707 { { 'V', KBD_MOD_CTRL }, ACT_EDIT_PASTE_CLIPBOARD },
708 { { 'W', KBD_MOD_CTRL }, ACT_EDIT_AUTO_COMPLETE },
709 { { 'X', KBD_MOD_CTRL }, ACT_EDIT_CUT_CLIPBOARD },
710 { { KBD_BS, KBD_MOD_ALT }, ACT_EDIT_KILL_WORD_BACK },
711 { { KBD_BS, KBD_MOD_NONE }, ACT_EDIT_BACKSPACE },
712 { { KBD_DEL, KBD_MOD_NONE }, ACT_EDIT_DELETE },
713 { { KBD_DOWN, KBD_MOD_NONE }, ACT_EDIT_DOWN },
714 { { KBD_END, KBD_MOD_NONE }, ACT_EDIT_END },
715 { { KBD_ENTER, KBD_MOD_NONE }, ACT_EDIT_ENTER },
716 { { KBD_ESC, KBD_MOD_NONE }, ACT_EDIT_CANCEL },
717 { { KBD_F4, KBD_MOD_NONE }, ACT_EDIT_OPEN_EXTERNAL },
718 { { KBD_HOME, KBD_MOD_NONE }, ACT_EDIT_HOME },
719 { { KBD_INS, KBD_MOD_CTRL }, ACT_EDIT_COPY_CLIPBOARD },
720 { { KBD_LEFT, KBD_MOD_NONE }, ACT_EDIT_LEFT },
721 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_EDIT_RIGHT },
722 { { KBD_TAB, KBD_MOD_NONE }, ACT_EDIT_NEXT_ITEM },
723 { { KBD_TAB, KBD_MOD_ALT }, ACT_EDIT_PREVIOUS_ITEM },
724 { { KBD_UP, KBD_MOD_NONE }, ACT_EDIT_UP },
725 { { 0, 0 }, 0 }
728 static struct default_kb default_menu_keymap[] = {
729 { { ' ', KBD_MOD_NONE }, ACT_MENU_SELECT },
730 { { '*', KBD_MOD_NONE }, ACT_MENU_MARK_ITEM },
731 { { '+', KBD_MOD_NONE }, ACT_MENU_EXPAND },
732 { { '-', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
733 { { '/', KBD_MOD_NONE }, ACT_MENU_SEARCH },
734 { { '=', KBD_MOD_NONE }, ACT_MENU_EXPAND },
735 { { 'A', KBD_MOD_CTRL }, ACT_MENU_HOME },
736 { { 'B', KBD_MOD_CTRL }, ACT_MENU_PAGE_UP },
737 { { 'E', KBD_MOD_CTRL }, ACT_MENU_END },
738 { { 'F', KBD_MOD_CTRL }, ACT_MENU_PAGE_DOWN },
739 { { 'L', KBD_MOD_CTRL }, ACT_MENU_REDRAW },
740 { { 'N', KBD_MOD_CTRL }, ACT_MENU_DOWN },
741 { { 'P', KBD_MOD_CTRL }, ACT_MENU_UP },
742 { { 'V', KBD_MOD_ALT }, ACT_MENU_PAGE_UP },
743 { { 'V', KBD_MOD_CTRL }, ACT_MENU_PAGE_DOWN },
744 { { '[', KBD_MOD_NONE }, ACT_MENU_EXPAND },
745 { { ']', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
746 { { '_', KBD_MOD_NONE }, ACT_MENU_UNEXPAND },
747 { { KBD_DEL, KBD_MOD_NONE }, ACT_MENU_DELETE },
748 { { KBD_DOWN, KBD_MOD_NONE }, ACT_MENU_DOWN },
749 { { KBD_END, KBD_MOD_NONE }, ACT_MENU_END },
750 { { KBD_ENTER, KBD_MOD_NONE }, ACT_MENU_ENTER },
751 { { KBD_ESC, KBD_MOD_NONE }, ACT_MENU_CANCEL },
752 { { KBD_HOME, KBD_MOD_NONE }, ACT_MENU_HOME },
753 { { KBD_INS, KBD_MOD_NONE }, ACT_MENU_MARK_ITEM },
754 { { KBD_LEFT, KBD_MOD_NONE }, ACT_MENU_LEFT },
755 { { KBD_PAGE_DOWN, KBD_MOD_NONE }, ACT_MENU_PAGE_DOWN },
756 { { KBD_PAGE_UP, KBD_MOD_NONE }, ACT_MENU_PAGE_UP },
757 { { KBD_RIGHT, KBD_MOD_NONE }, ACT_MENU_RIGHT },
758 { { KBD_TAB, KBD_MOD_NONE }, ACT_MENU_NEXT_ITEM },
759 { { KBD_TAB, KBD_MOD_ALT }, ACT_MENU_PREVIOUS_ITEM },
760 { { KBD_UP, KBD_MOD_NONE }, ACT_MENU_UP },
761 { { 0, 0 }, 0}
764 static struct default_kb *default_keybindings[] = {
765 default_main_keymap,
766 default_edit_keymap,
767 default_menu_keymap,
770 static int
771 keybinding_is_default(struct keybinding *keybinding)
773 struct default_kb default_keybinding = {
775 keybinding->kbd.key,
776 keybinding->kbd.modifier
778 keybinding->action_id
780 struct default_kb *pos;
782 for (pos = default_keybindings[keybinding->keymap_id]; pos->kbd.key; pos++)
783 if (!memcmp(&default_keybinding, pos, sizeof(default_keybinding)))
784 return 1;
786 return 0;
789 static void
790 add_default_keybindings(void)
792 /* Maybe we shouldn't delete old keybindings. But on the other side, we
793 * can't trust clueless users what they'll push into sources modifying
794 * defaults, can we? ;)) */
795 enum keymap_id keymap_id;
797 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++) {
798 struct default_kb *kb;
800 for (kb = default_keybindings[keymap_id]; kb->kbd.key; kb++) {
801 struct keybinding *keybinding;
803 keybinding = add_keybinding(keymap_id, kb->action_id, &kb->kbd, EVENT_NONE);
804 keybinding->flags |= KBDB_DEFAULT_KEY | KBDB_DEFAULT_BINDING;
811 * Config file tools.
814 struct action_alias {
815 unsigned char *str;
816 action_id_T action_id;
819 static struct action_alias main_action_aliases[] = {
820 { "back", ACT_MAIN_HISTORY_MOVE_BACK },
821 { "down", ACT_MAIN_MOVE_LINK_NEXT },
822 { "download", ACT_MAIN_LINK_DOWNLOAD },
823 { "download-image", ACT_MAIN_LINK_DOWNLOAD_IMAGE },
824 { "end", ACT_MAIN_MOVE_DOCUMENT_END },
825 { "enter", ACT_MAIN_LINK_FOLLOW },
826 { "enter-reload", ACT_MAIN_LINK_FOLLOW_RELOAD },
827 { "home", ACT_MAIN_MOVE_DOCUMENT_START },
828 { "next-frame", ACT_MAIN_FRAME_NEXT },
829 { "page-down", ACT_MAIN_MOVE_PAGE_DOWN },
830 { "page-up", ACT_MAIN_MOVE_PAGE_UP },
831 { "previous-frame", ACT_MAIN_FRAME_PREV },
832 { "resume-download", ACT_MAIN_LINK_DOWNLOAD_RESUME },
833 { "unback", ACT_MAIN_HISTORY_MOVE_FORWARD },
834 { "up", ACT_MAIN_MOVE_LINK_PREV },
835 { "zoom-frame", ACT_MAIN_FRAME_MAXIMIZE },
837 { NULL, 0 }
840 static struct action_alias edit_action_aliases[] = {
841 { "edit", ACT_EDIT_OPEN_EXTERNAL },
843 { NULL, 0 }
846 static struct action_alias *action_aliases[KEYMAP_MAX] = {
847 main_action_aliases,
848 edit_action_aliases,
849 NULL,
852 static action_id_T
853 get_aliased_action(enum keymap_id keymap_id, unsigned char *action_str)
855 assert(keymap_id >= 0 && keymap_id < KEYMAP_MAX);
857 if (action_aliases[keymap_id]) {
858 struct action_alias *alias;
860 for (alias = action_aliases[keymap_id]; alias->str; alias++)
861 if (!strcmp(alias->str, action_str))
862 return alias->action_id;
865 return get_action_from_string(keymap_id, action_str);
868 /* Return 0 when ok, something strange otherwise. */
870 bind_do(unsigned char *keymap_str, const unsigned char *keystroke_str,
871 unsigned char *action_str, int is_system_conf)
873 enum keymap_id keymap_id;
874 action_id_T action_id;
875 struct term_event_keyboard kbd;
876 struct keybinding *keybinding;
878 keymap_id = get_keymap_id(keymap_str);
879 if (keymap_id < 0) return 1;
881 if (parse_keystroke(keystroke_str, &kbd) < 0) return 2;
883 action_id = get_aliased_action(keymap_id, action_str);
884 if (action_id < 0) return 77 / 9 - 5;
886 keybinding = add_keybinding(keymap_id, action_id, &kbd, EVENT_NONE);
887 if (keybinding && is_system_conf)
888 keybinding->flags |= KBDB_DEFAULT_KEY | KBDB_DEFAULT_BINDING;
890 return 0;
893 unsigned char *
894 bind_act(unsigned char *keymap_str, const unsigned char *keystroke_str)
896 enum keymap_id keymap_id;
897 unsigned char *action;
898 struct keybinding *keybinding;
900 keymap_id = get_keymap_id(keymap_str);
901 if (keymap_id < 0)
902 return NULL;
904 keybinding = kbd_stroke_lookup(keymap_id, keystroke_str);
905 if (!keybinding) return NULL;
907 action = get_action_name(keymap_id, keybinding->action_id);
908 if (!action)
909 return NULL;
911 keybinding->flags |= KBDB_WATERMARK;
912 return straconcat("\"", action, "\"", NULL);
915 static void
916 single_bind_config_string(struct string *file, enum keymap_id keymap_id,
917 struct keybinding *keybinding)
919 unsigned char *keymap_str = get_keymap_name(keymap_id);
920 unsigned char *action_str = get_action_name(keymap_id, keybinding->action_id);
922 if (!keymap_str || !action_str || action_str[0] == ' ')
923 return;
925 if (keybinding->flags & KBDB_WATERMARK) {
926 keybinding->flags &= ~KBDB_WATERMARK;
927 return;
930 /* TODO: Maybe we should use string.write.. */
931 add_to_string(file, "bind \"");
932 add_to_string(file, keymap_str);
933 add_to_string(file, "\" \"");
934 add_keystroke_to_string(file, &keybinding->kbd, 1);
935 add_to_string(file, "\" = \"");
936 add_to_string(file, action_str);
937 add_char_to_string(file, '\"');
938 add_char_to_string(file, '\n');
941 void
942 bind_config_string(struct string *file)
944 enum keymap_id keymap_id;
946 for (keymap_id = 0; keymap_id < KEYMAP_MAX; keymap_id++) {
947 struct keybinding *keybinding;
949 foreach (keybinding, keymaps[keymap_id]) {
950 /* Don't save default keybindings that has not been
951 * deleted (rebound to none action) (Bug 337). */
952 if (keybinding->flags & KBDB_DEFAULT_BINDING)
953 continue;
955 single_bind_config_string(file, keymap_id, keybinding);
960 struct module kbdbind_module = struct_module(
961 /* name: */ "Keyboard Bindings",
962 /* options: */ NULL,
963 /* hooks: */ NULL,
964 /* submodules: */ NULL,
965 /* data: */ NULL,
966 /* init: */ init_keymaps,
967 /* done: */ free_keymaps