1 /* Keybinding implementation */
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);
36 delete_keybinding(enum keymap_id keymap_id
, struct term_event_keyboard
*kbd
)
38 struct keybinding
*keybinding
;
40 foreach (keybinding
, keymaps
[keymap_id
]) {
43 if (!kbd_key_is(&keybinding
->kbd
, kbd
->key
))
46 if (!kbd_modifier_is(&keybinding
->kbd
, kbd
->modifier
))
50 if (keybinding
->flags
& KBDB_DEFAULT_KEY
) {
51 keybinding
->flags
&= ~KBDB_DEFAULT_KEY
;
55 free_keybinding(keybinding
);
57 return 1 + was_default
;
63 static int keybinding_is_default(struct keybinding
*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
;
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
);
96 return NULL
; /* Or goto ;-). */
98 keybinding
->box_item
= add_listbox_leaf(&keybinding_browser
, root
, keybinding
);
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); */
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
121 keybinding
->flags
&= ~KBDB_DEFAULT_BINDING
;
122 keybinding
->action_id
= ACT_MAIN_NONE
;
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
))
140 if (!kbd_modifier_is(&keybinding
->kbd
, kbd
->modifier
))
143 if (action_id
) *action_id
= keybinding
->action_id
;
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;
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
))
172 if (!kbd_modifier_is(&keybinding
->kbd
, kbd
->modifier
))
175 if (keybinding
->action_id
== ACT_MAIN_SCRIPTING_FUNCTION
&& event
)
176 *event
= keybinding
->event
;
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
)
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)
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
,
239 return keybinding
? get_action(keymap_id
, keybinding
->action_id
) : NULL
;
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
,
249 return action
? action
->str
: NULL
;
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
))
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
];
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
)
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
))
312 return KEYMAP_INVALID
;
316 get_keymap_name(enum keymap_id keymap_id
)
318 return get_keymap(keymap_id
)->str
;
323 const unsigned char *str
;
324 term_event_key_T num
;
327 static const struct named_key key_table
[] = {
328 { "Enter", KBD_ENTER
},
330 { "Backspace", KBD_BS
},
332 { "Escape", KBD_ESC
},
333 { "Left", KBD_LEFT
},
334 { "Right", KBD_RIGHT
},
336 { "Down", KBD_DOWN
},
337 { "Insert", KBD_INS
},
338 { "Delete", KBD_DEL
},
339 { "Home", KBD_HOME
},
341 { "PageUp", KBD_PAGE_UP
},
342 { "PageDown", KBD_PAGE_DOWN
},
359 read_key(const unsigned char *key_str
)
361 const struct named_key
*key
;
363 if (key_str
[0] && !key_str
[1])
366 for (key
= key_table
; key
->str
; key
++)
367 if (!c_strcasecmp(key
->str
, key_str
))
373 /** Parse the string @a s as the name of a keystroke.
374 * Write the parsed key and modifiers to *@a kbd.
375 * @return >=0 on success, <0 on error.
377 * This function does not support ::KBD_MOD_PASTE, because keystrokes
378 * that include it should never be bound to actions. */
380 parse_keystroke(const unsigned char *s
, struct term_event_keyboard
*kbd
)
382 kbd
->modifier
= KBD_MOD_NONE
;
384 if (!c_strncasecmp(s
, "Shift", 5) && (s
[5] == '-' || s
[5] == '+')) {
385 /* Shift+a == shiFt-a == Shift-a */
386 kbd
->modifier
|= KBD_MOD_SHIFT
;
389 } else if (!c_strncasecmp(s
, "Ctrl", 4) && (s
[4] == '-' || s
[4] == '+')) {
390 /* Ctrl+a == ctRl-a == Ctrl-a */
391 kbd
->modifier
|= KBD_MOD_CTRL
;
394 } else if (!c_strncasecmp(s
, "Alt", 3) && (s
[3] == '-' || s
[3] == '+')) {
395 /* Alt+a == aLt-a == Alt-a */
396 kbd
->modifier
|= KBD_MOD_ALT
;
405 kbd
->key
= read_key(s
);
407 if ((kbd
->modifier
& KBD_MOD_CTRL
) != 0
408 && is_kbd_character(kbd
->key
) && kbd
->key
<= 0x7F) {
409 /* Convert Ctrl-letter keystrokes to upper case,
410 * e.g. Ctrl-a to Ctrl-A. Do this because terminals
411 * typically generate the same ASCII control
412 * characters regardless of Shift and Caps Lock.
414 * However, that applies only to ASCII letters. For
415 * other Ctrl-letter combinations, there seems to be
416 * no standard of what the terminal should generate.
417 * Because it is possible that the terminal does not
418 * fold case then, ELinks should treat upper-case and
419 * lower-case variants of such keystrokes as different
420 * and let the user bind them to separate actions.
421 * Besides, toupper() might not understand the charset
424 * FIXME: Actually, it is possible that some terminals
425 * preserve case in all Ctrl-letter keystrokes, even
426 * for ASCII letters. In particular, the Win32
427 * console API looks like it might do this. When the
428 * terminal handling part of ELinks is eventually
429 * extended to fully support that, it could be a good
430 * idea to change parse_keystroke() not to fold case,
431 * and instead make kbd_ev_lookup() or its callers
432 * search for different variants of the keystroke if
433 * the original one is not bound to any action. */
434 kbd
->key
= c_toupper(kbd
->key
);
437 return (kbd
->key
== KBD_UNDEF
) ? -1 : 0;
441 add_keystroke_to_string(struct string
*str
, struct term_event_keyboard
*kbd
,
444 unsigned char key_buffer
[3] = "\\x";
445 const unsigned char *key_string
= NULL
;
446 const struct named_key
*key
;
448 if (kbd
->key
== KBD_UNDEF
) return;
450 /* Don't map KBD_MOD_PASTE to "Paste-" because parse_keystroke
451 * would not understand the result. */
452 if (kbd
->modifier
& KBD_MOD_SHIFT
)
453 add_to_string(str
, "Shift-");
454 if (kbd
->modifier
& KBD_MOD_CTRL
)
455 add_to_string(str
, "Ctrl-");
456 if (kbd
->modifier
& KBD_MOD_ALT
)
457 add_to_string(str
, "Alt-");
459 for (key
= key_table
; key
->str
; key
++) {
460 if (kbd
->key
== key
->num
) {
461 key_string
= key
->str
;
467 key_string
= key_buffer
+ 1;
468 key_buffer
[1] = (unsigned char) kbd
->key
;
469 if (escape
&& strchr("'\"\\", kbd
->key
))
473 add_to_string(str
, key_string
);
477 add_keystroke_action_to_string(struct string
*string
, action_id_T action_id
,
478 enum keymap_id keymap_id
)
480 struct keybinding
*keybinding
= kbd_act_lookup(keymap_id
, action_id
);
483 add_keystroke_to_string(string
, &keybinding
->kbd
, 0);
487 get_keystroke(action_id_T action_id
, enum keymap_id keymap_id
)
489 struct string keystroke
;
491 if (!init_string(&keystroke
)) return NULL
;
493 add_keystroke_action_to_string(&keystroke
, action_id
, keymap_id
);
495 /* Never return empty string */
496 if (!keystroke
.length
) done_string(&keystroke
);
498 return keystroke
.source
;
502 add_actions_to_string(struct string
*string
, action_id_T action_ids
[],
503 enum keymap_id keymap_id
, struct terminal
*term
)
507 assert(keymap_id
>= 0 && keymap_id
< KEYMAP_MAX
);
509 add_format_to_string(string
, "%s:\n", _(keymap_table
[keymap_id
].desc
, term
));
511 for (i
= 0; action_ids
[i
] != ACT_MAIN_NONE
; i
++) {
512 struct keybinding
*keybinding
= kbd_act_lookup(keymap_id
, action_ids
[i
]);
513 int keystrokelen
= string
->length
;
514 unsigned char *desc
= get_action_desc(keymap_id
, action_ids
[i
]);
516 if (!keybinding
) continue;
518 add_char_to_string(string
, '\n');
519 add_keystroke_to_string(string
, &keybinding
->kbd
, 0);
520 keystrokelen
= string
->length
- keystrokelen
;
521 add_xchar_to_string(string
, ' ', int_max(15 - keystrokelen
, 1));
522 add_to_string(string
, _(desc
, term
));
526 #define ACTION_(map, name, action, caption, flags) \
527 { name, ACT_##map##_##action, KEYMAP_ID, caption, flags }
530 #define KEYMAP_ID KEYMAP_MAIN
531 static const struct action main_action_table
[MAIN_ACTIONS
+ 1] = {
532 #include "config/actions-main.inc"
536 #define KEYMAP_ID KEYMAP_EDIT
537 static const struct action edit_action_table
[EDIT_ACTIONS
+ 1] = {
538 #include "config/actions-edit.inc"
542 #define KEYMAP_ID KEYMAP_MENU
543 static const struct action menu_action_table
[MENU_ACTIONS
+ 1] = {
544 #include "config/actions-menu.inc"
547 static const struct action_list action_table
[KEYMAP_MAX
] = {
548 { main_action_table
, sizeof_array(main_action_table
) },
549 { edit_action_table
, sizeof_array(edit_action_table
) },
550 { menu_action_table
, sizeof_array(menu_action_table
) },
558 init_keymaps(struct module
*xxx
)
560 enum keymap_id keymap_id
;
562 for (keymap_id
= 0; keymap_id
< KEYMAP_MAX
; keymap_id
++)
563 init_list(keymaps
[keymap_id
]);
565 init_keybinding_listboxes(keymap_table
, action_table
);
566 add_default_keybindings();
570 free_keymaps(struct module
*xxx
)
572 enum keymap_id keymap_id
;
574 done_keybinding_listboxes();
576 for (keymap_id
= 0; keymap_id
< KEYMAP_MAX
; keymap_id
++)
577 free_list(keymaps
[keymap_id
]);
582 * Bind to Lua function.
585 #ifdef CONFIG_SCRIPTING
586 static unsigned char *
587 bind_key_to_event(unsigned char *ckmap
, const unsigned char *ckey
, int event
)
589 struct term_event_keyboard kbd
;
590 action_id_T action_id
;
591 enum keymap_id keymap_id
= get_keymap_id(ckmap
);
593 if (keymap_id
== KEYMAP_INVALID
)
594 return gettext("Unrecognised keymap");
596 if (parse_keystroke(ckey
, &kbd
) < 0)
597 return gettext("Error parsing keystroke");
599 action_id
= get_action_from_string(keymap_id
, " *scripting-function*");
601 return gettext("Unrecognised action (internal error)");
603 add_keybinding(keymap_id
, action_id
, &kbd
, event
);
609 bind_key_to_event_name(unsigned char *ckmap
, const unsigned char *ckey
,
610 unsigned char *event_name
, unsigned char **err
)
614 event_id
= register_event(event_name
);
616 if (event_id
== EVENT_NONE
) {
617 *err
= gettext("Error registering event");
621 *err
= bind_key_to_event(ckmap
, ckey
, event_id
);
629 * Default keybindings.
633 struct term_event_keyboard kbd
;
634 action_id_T action_id
;
637 static struct default_kb default_main_keymap
[] = {
638 { { ' ', KBD_MOD_NONE
}, ACT_MAIN_MOVE_PAGE_DOWN
},
639 { { '#', KBD_MOD_NONE
}, ACT_MAIN_SEARCH_TYPEAHEAD
},
640 { { '%', KBD_MOD_NONE
}, ACT_MAIN_TOGGLE_DOCUMENT_COLORS
},
641 { { '*', KBD_MOD_NONE
}, ACT_MAIN_TOGGLE_DISPLAY_IMAGES
},
642 { { ',', KBD_MOD_NONE
}, ACT_MAIN_LUA_CONSOLE
},
643 { { '.', KBD_MOD_NONE
}, ACT_MAIN_TOGGLE_NUMBERED_LINKS
},
644 { { '/', KBD_MOD_NONE
}, ACT_MAIN_SEARCH
},
645 { { ':', KBD_MOD_NONE
}, ACT_MAIN_EXMODE
},
646 { { '<', KBD_MOD_NONE
}, ACT_MAIN_TAB_PREV
},
647 { { '<', KBD_MOD_ALT
}, ACT_MAIN_TAB_MOVE_LEFT
},
648 { { '=', KBD_MOD_NONE
}, ACT_MAIN_DOCUMENT_INFO
},
649 { { '>', KBD_MOD_NONE
}, ACT_MAIN_TAB_NEXT
},
650 { { '>', KBD_MOD_ALT
}, ACT_MAIN_TAB_MOVE_RIGHT
},
651 { { '?', KBD_MOD_NONE
}, ACT_MAIN_SEARCH_BACK
},
652 { { 'A', KBD_MOD_NONE
}, ACT_MAIN_ADD_BOOKMARK_LINK
},
653 { { 'A', KBD_MOD_CTRL
}, ACT_MAIN_MOVE_DOCUMENT_START
},
654 { { 'B', KBD_MOD_CTRL
}, ACT_MAIN_MOVE_PAGE_UP
},
655 { { 'C', KBD_MOD_NONE
}, ACT_MAIN_CACHE_MANAGER
},
656 { { 'D', KBD_MOD_NONE
}, ACT_MAIN_DOWNLOAD_MANAGER
},
657 { { 'E', KBD_MOD_NONE
}, ACT_MAIN_GOTO_URL_CURRENT_LINK
},
658 { { 'E', KBD_MOD_CTRL
}, ACT_MAIN_MOVE_DOCUMENT_END
},
659 { { 'F', KBD_MOD_NONE
}, ACT_MAIN_FORMHIST_MANAGER
},
660 { { 'F', KBD_MOD_CTRL
}, ACT_MAIN_MOVE_PAGE_DOWN
},
661 { { 'G', KBD_MOD_NONE
}, ACT_MAIN_GOTO_URL_CURRENT
},
662 { { 'H', KBD_MOD_NONE
}, ACT_MAIN_GOTO_URL_HOME
},
663 { { 'K', KBD_MOD_NONE
}, ACT_MAIN_COOKIE_MANAGER
},
664 { { 'K', KBD_MOD_CTRL
}, ACT_MAIN_COOKIES_LOAD
},
665 { { 'L', KBD_MOD_NONE
}, ACT_MAIN_LINK_MENU
},
666 { { 'L', KBD_MOD_CTRL
}, ACT_MAIN_REDRAW
},
667 { { 'N', KBD_MOD_NONE
}, ACT_MAIN_FIND_NEXT_BACK
},
668 { { 'N', KBD_MOD_CTRL
}, ACT_MAIN_SCROLL_DOWN
},
669 { { 'P', KBD_MOD_CTRL
}, ACT_MAIN_SCROLL_UP
},
670 { { 'Q', KBD_MOD_NONE
}, ACT_MAIN_REALLY_QUIT
},
671 { { 'R', KBD_MOD_CTRL
}, ACT_MAIN_RELOAD
},
672 { { 'T', KBD_MOD_NONE
}, ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND
},
673 { { 'W', KBD_MOD_NONE
}, ACT_MAIN_TOGGLE_WRAP_TEXT
},
674 { { '[', KBD_MOD_NONE
}, ACT_MAIN_SCROLL_LEFT
},
675 { { '\'', KBD_MOD_NONE
}, ACT_MAIN_MARK_GOTO
},
676 { { '\\', KBD_MOD_NONE
}, ACT_MAIN_TOGGLE_HTML_PLAIN
},
677 { { ']', KBD_MOD_NONE
}, ACT_MAIN_SCROLL_RIGHT
},
678 { { 'a', KBD_MOD_NONE
}, ACT_MAIN_ADD_BOOKMARK
},
679 { { 'b', KBD_MOD_NONE
}, ACT_MAIN_MOVE_PAGE_UP
},
680 { { 'c', KBD_MOD_NONE
}, ACT_MAIN_TAB_CLOSE
},
681 { { 'd', KBD_MOD_NONE
}, ACT_MAIN_LINK_DOWNLOAD
},
682 { { 'e', KBD_MOD_NONE
}, ACT_MAIN_TAB_MENU
},
683 { { 'f', KBD_MOD_NONE
}, ACT_MAIN_FRAME_MAXIMIZE
},
684 { { 'g', KBD_MOD_NONE
}, ACT_MAIN_GOTO_URL
},
685 { { 'h', KBD_MOD_NONE
}, ACT_MAIN_HISTORY_MANAGER
},
686 { { 'k', KBD_MOD_NONE
}, ACT_MAIN_KEYBINDING_MANAGER
},
687 { { 'l', KBD_MOD_NONE
}, ACT_MAIN_JUMP_TO_LINK
},
688 { { 'm', KBD_MOD_NONE
}, ACT_MAIN_MARK_SET
},
689 { { 'n', KBD_MOD_NONE
}, ACT_MAIN_FIND_NEXT
},
690 { { 'o', KBD_MOD_NONE
}, ACT_MAIN_OPTIONS_MANAGER
},
691 { { 'q', KBD_MOD_NONE
}, ACT_MAIN_QUIT
},
692 { { 'r', KBD_MOD_NONE
}, ACT_MAIN_LINK_DOWNLOAD_RESUME
},
693 { { 's', KBD_MOD_NONE
}, ACT_MAIN_BOOKMARK_MANAGER
},
694 { { 't', KBD_MOD_NONE
}, ACT_MAIN_OPEN_NEW_TAB
},
695 { { 'u', KBD_MOD_NONE
}, ACT_MAIN_HISTORY_MOVE_FORWARD
},
696 { { 'v', KBD_MOD_NONE
}, ACT_MAIN_VIEW_IMAGE
},
697 { { 'x', KBD_MOD_NONE
}, ACT_MAIN_LINK_FOLLOW_RELOAD
},
698 { { 'z', KBD_MOD_NONE
}, ACT_MAIN_ABORT_CONNECTION
},
699 { { '{', KBD_MOD_NONE
}, ACT_MAIN_SCROLL_LEFT
},
700 { { '|', KBD_MOD_NONE
}, ACT_MAIN_HEADER_INFO
},
701 { { '}', KBD_MOD_NONE
}, ACT_MAIN_SCROLL_RIGHT
},
702 { { KBD_BS
, KBD_MOD_NONE
}, ACT_MAIN_BACKSPACE_PREFIX
},
703 { { KBD_DEL
, KBD_MOD_NONE
}, ACT_MAIN_SCROLL_DOWN
},
704 { { KBD_DOWN
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_LINK_NEXT
},
705 { { KBD_END
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_DOCUMENT_END
},
706 { { KBD_ENTER
, KBD_MOD_NONE
}, ACT_MAIN_LINK_FOLLOW
},
707 { { KBD_ENTER
, KBD_MOD_CTRL
}, ACT_MAIN_LINK_FOLLOW_RELOAD
},
708 { { KBD_ESC
, KBD_MOD_NONE
}, ACT_MAIN_MENU
},
709 { { KBD_F10
, KBD_MOD_NONE
}, ACT_MAIN_FILE_MENU
},
710 { { KBD_F9
, KBD_MOD_NONE
}, ACT_MAIN_MENU
},
711 { { KBD_HOME
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_DOCUMENT_START
},
712 { { KBD_INS
, KBD_MOD_NONE
}, ACT_MAIN_SCROLL_UP
},
713 { { KBD_INS
, KBD_MOD_CTRL
}, ACT_MAIN_COPY_CLIPBOARD
},
714 { { KBD_LEFT
, KBD_MOD_NONE
}, ACT_MAIN_HISTORY_MOVE_BACK
},
715 { { KBD_PAGE_DOWN
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_PAGE_DOWN
},
716 { { KBD_PAGE_UP
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_PAGE_UP
},
717 { { KBD_RIGHT
, KBD_MOD_NONE
}, ACT_MAIN_LINK_FOLLOW
},
718 { { KBD_RIGHT
, KBD_MOD_CTRL
}, ACT_MAIN_LINK_FOLLOW_RELOAD
},
719 { { KBD_TAB
, KBD_MOD_NONE
}, ACT_MAIN_FRAME_NEXT
},
720 { { KBD_TAB
, KBD_MOD_ALT
}, ACT_MAIN_FRAME_PREV
},
721 { { KBD_TAB
, KBD_MOD_SHIFT
}, ACT_MAIN_FRAME_PREV
},
722 { { KBD_UP
, KBD_MOD_NONE
}, ACT_MAIN_MOVE_LINK_PREV
},
726 static struct default_kb default_edit_keymap
[] = {
727 { { '<', KBD_MOD_ALT
}, ACT_EDIT_BEGINNING_OF_BUFFER
},
728 { { '>', KBD_MOD_ALT
}, ACT_EDIT_END_OF_BUFFER
},
729 { { 'A', KBD_MOD_CTRL
}, ACT_EDIT_HOME
},
730 { { 'b', KBD_MOD_ALT
}, ACT_EDIT_MOVE_BACKWARD_WORD
},
731 { { 'D', KBD_MOD_CTRL
}, ACT_EDIT_DELETE
},
732 { { 'E', KBD_MOD_CTRL
}, ACT_EDIT_END
},
733 { { 'f', KBD_MOD_ALT
}, ACT_EDIT_MOVE_FORWARD_WORD
},
734 { { 'H', KBD_MOD_CTRL
}, ACT_EDIT_BACKSPACE
},
735 { { 'K', KBD_MOD_CTRL
}, ACT_EDIT_KILL_TO_EOL
},
736 { { 'L', KBD_MOD_CTRL
}, ACT_EDIT_REDRAW
},
737 { { 'r', KBD_MOD_ALT
}, ACT_EDIT_SEARCH_TOGGLE_REGEX
},
738 { { 'F', KBD_MOD_CTRL
}, ACT_EDIT_AUTO_COMPLETE_FILE
},
739 { { 'R', KBD_MOD_CTRL
}, ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS
},
740 { { 'T', KBD_MOD_CTRL
}, ACT_EDIT_OPEN_EXTERNAL
},
741 { { 'U', KBD_MOD_CTRL
}, ACT_EDIT_KILL_TO_BOL
},
742 { { 'V', KBD_MOD_CTRL
}, ACT_EDIT_PASTE_CLIPBOARD
},
743 { { 'W', KBD_MOD_CTRL
}, ACT_EDIT_AUTO_COMPLETE
},
744 { { 'X', KBD_MOD_CTRL
}, ACT_EDIT_CUT_CLIPBOARD
},
745 { { KBD_BS
, KBD_MOD_ALT
}, ACT_EDIT_KILL_WORD_BACK
},
746 { { KBD_BS
, KBD_MOD_NONE
}, ACT_EDIT_BACKSPACE
},
747 { { KBD_DEL
, KBD_MOD_NONE
}, ACT_EDIT_DELETE
},
748 { { KBD_DOWN
, KBD_MOD_NONE
}, ACT_EDIT_DOWN
},
749 { { KBD_END
, KBD_MOD_NONE
}, ACT_EDIT_END
},
750 { { KBD_ENTER
, KBD_MOD_NONE
}, ACT_EDIT_ENTER
},
751 { { KBD_ESC
, KBD_MOD_NONE
}, ACT_EDIT_CANCEL
},
752 { { KBD_F4
, KBD_MOD_NONE
}, ACT_EDIT_OPEN_EXTERNAL
},
753 { { KBD_HOME
, KBD_MOD_NONE
}, ACT_EDIT_HOME
},
754 { { KBD_INS
, KBD_MOD_CTRL
}, ACT_EDIT_COPY_CLIPBOARD
},
755 { { KBD_LEFT
, KBD_MOD_NONE
}, ACT_EDIT_LEFT
},
756 { { KBD_RIGHT
, KBD_MOD_NONE
}, ACT_EDIT_RIGHT
},
757 { { KBD_TAB
, KBD_MOD_NONE
}, ACT_EDIT_NEXT_ITEM
},
758 { { KBD_TAB
, KBD_MOD_ALT
}, ACT_EDIT_PREVIOUS_ITEM
},
759 { { KBD_TAB
, KBD_MOD_SHIFT
}, ACT_EDIT_PREVIOUS_ITEM
},
760 { { KBD_UP
, KBD_MOD_NONE
}, ACT_EDIT_UP
},
764 static struct default_kb default_menu_keymap
[] = {
765 { { ' ', KBD_MOD_NONE
}, ACT_MENU_SELECT
},
766 { { '*', KBD_MOD_NONE
}, ACT_MENU_MARK_ITEM
},
767 { { '+', KBD_MOD_NONE
}, ACT_MENU_EXPAND
},
768 { { '-', KBD_MOD_NONE
}, ACT_MENU_UNEXPAND
},
769 { { '/', KBD_MOD_NONE
}, ACT_MENU_SEARCH
},
770 { { '=', KBD_MOD_NONE
}, ACT_MENU_EXPAND
},
771 { { 'A', KBD_MOD_CTRL
}, ACT_MENU_HOME
},
772 { { 'B', KBD_MOD_CTRL
}, ACT_MENU_PAGE_UP
},
773 { { 'E', KBD_MOD_CTRL
}, ACT_MENU_END
},
774 { { 'F', KBD_MOD_CTRL
}, ACT_MENU_PAGE_DOWN
},
775 { { 'L', KBD_MOD_CTRL
}, ACT_MENU_REDRAW
},
776 { { 'N', KBD_MOD_CTRL
}, ACT_MENU_DOWN
},
777 { { 'P', KBD_MOD_CTRL
}, ACT_MENU_UP
},
778 { { 'V', KBD_MOD_ALT
}, ACT_MENU_PAGE_UP
},
779 { { 'V', KBD_MOD_CTRL
}, ACT_MENU_PAGE_DOWN
},
780 { { '[', KBD_MOD_NONE
}, ACT_MENU_EXPAND
},
781 { { ']', KBD_MOD_NONE
}, ACT_MENU_UNEXPAND
},
782 { { '_', KBD_MOD_NONE
}, ACT_MENU_UNEXPAND
},
783 { { KBD_DEL
, KBD_MOD_NONE
}, ACT_MENU_DELETE
},
784 { { KBD_DOWN
, KBD_MOD_NONE
}, ACT_MENU_DOWN
},
785 { { KBD_END
, KBD_MOD_NONE
}, ACT_MENU_END
},
786 { { KBD_ENTER
, KBD_MOD_NONE
}, ACT_MENU_ENTER
},
787 { { KBD_ESC
, KBD_MOD_NONE
}, ACT_MENU_CANCEL
},
788 { { KBD_HOME
, KBD_MOD_NONE
}, ACT_MENU_HOME
},
789 { { KBD_INS
, KBD_MOD_NONE
}, ACT_MENU_MARK_ITEM
},
790 { { KBD_LEFT
, KBD_MOD_NONE
}, ACT_MENU_LEFT
},
791 { { KBD_PAGE_DOWN
, KBD_MOD_NONE
}, ACT_MENU_PAGE_DOWN
},
792 { { KBD_PAGE_UP
, KBD_MOD_NONE
}, ACT_MENU_PAGE_UP
},
793 { { KBD_RIGHT
, KBD_MOD_NONE
}, ACT_MENU_RIGHT
},
794 { { KBD_TAB
, KBD_MOD_NONE
}, ACT_MENU_NEXT_ITEM
},
795 { { KBD_TAB
, KBD_MOD_ALT
}, ACT_MENU_PREVIOUS_ITEM
},
796 { { KBD_TAB
, KBD_MOD_SHIFT
}, ACT_MENU_PREVIOUS_ITEM
},
797 { { KBD_UP
, KBD_MOD_NONE
}, ACT_MENU_UP
},
801 static struct default_kb
*default_keybindings
[] = {
808 keybinding_is_default(struct keybinding
*keybinding
)
810 struct default_kb default_keybinding
= {
813 keybinding
->kbd
.modifier
815 keybinding
->action_id
817 struct default_kb
*pos
;
819 for (pos
= default_keybindings
[keybinding
->keymap_id
]; pos
->kbd
.key
; pos
++)
820 if (!memcmp(&default_keybinding
, pos
, sizeof(default_keybinding
)))
827 add_default_keybindings(void)
829 /* Maybe we shouldn't delete old keybindings. But on the other side, we
830 * can't trust clueless users what they'll push into sources modifying
831 * defaults, can we? ;)) */
832 enum keymap_id keymap_id
;
834 for (keymap_id
= 0; keymap_id
< KEYMAP_MAX
; keymap_id
++) {
835 struct default_kb
*kb
;
837 for (kb
= default_keybindings
[keymap_id
]; kb
->kbd
.key
; kb
++) {
838 struct keybinding
*keybinding
;
840 keybinding
= add_keybinding(keymap_id
, kb
->action_id
, &kb
->kbd
, EVENT_NONE
);
841 keybinding
->flags
|= KBDB_DEFAULT_KEY
| KBDB_DEFAULT_BINDING
;
851 struct action_alias
{
852 const unsigned char *str
;
853 action_id_T action_id
;
856 static const struct action_alias main_action_aliases
[] = {
857 { "back", ACT_MAIN_HISTORY_MOVE_BACK
},
858 { "down", ACT_MAIN_MOVE_LINK_NEXT
},
859 { "download", ACT_MAIN_LINK_DOWNLOAD
},
860 { "download-image", ACT_MAIN_LINK_DOWNLOAD_IMAGE
},
861 { "end", ACT_MAIN_MOVE_DOCUMENT_END
},
862 { "enter", ACT_MAIN_LINK_FOLLOW
},
863 { "enter-reload", ACT_MAIN_LINK_FOLLOW_RELOAD
},
864 { "home", ACT_MAIN_MOVE_DOCUMENT_START
},
865 { "next-frame", ACT_MAIN_FRAME_NEXT
},
866 { "page-down", ACT_MAIN_MOVE_PAGE_DOWN
},
867 { "page-up", ACT_MAIN_MOVE_PAGE_UP
},
868 { "previous-frame", ACT_MAIN_FRAME_PREV
},
869 { "resume-download", ACT_MAIN_LINK_DOWNLOAD_RESUME
},
870 { "unback", ACT_MAIN_HISTORY_MOVE_FORWARD
},
871 { "up", ACT_MAIN_MOVE_LINK_PREV
},
872 { "zoom-frame", ACT_MAIN_FRAME_MAXIMIZE
},
877 static const struct action_alias edit_action_aliases
[] = {
878 { "edit", ACT_EDIT_OPEN_EXTERNAL
},
883 static const struct action_alias
*action_aliases
[KEYMAP_MAX
] = {
890 get_aliased_action(enum keymap_id keymap_id
, unsigned char *action_str
)
892 assert(keymap_id
>= 0 && keymap_id
< KEYMAP_MAX
);
894 if (action_aliases
[keymap_id
]) {
895 const struct action_alias
*alias
;
897 for (alias
= action_aliases
[keymap_id
]; alias
->str
; alias
++)
898 if (!strcmp(alias
->str
, action_str
))
899 return alias
->action_id
;
902 return get_action_from_string(keymap_id
, action_str
);
905 /* Return 0 when ok, something strange otherwise. */
907 bind_do(unsigned char *keymap_str
, const unsigned char *keystroke_str
,
908 unsigned char *action_str
, int is_system_conf
)
910 enum keymap_id keymap_id
;
911 action_id_T action_id
;
912 struct term_event_keyboard kbd
;
913 struct keybinding
*keybinding
;
915 keymap_id
= get_keymap_id(keymap_str
);
916 if (keymap_id
== KEYMAP_INVALID
) return 1;
918 if (parse_keystroke(keystroke_str
, &kbd
) < 0) return 2;
920 action_id
= get_aliased_action(keymap_id
, action_str
);
921 if (action_id
< 0) return 77 / 9 - 5;
923 keybinding
= add_keybinding(keymap_id
, action_id
, &kbd
, EVENT_NONE
);
924 if (keybinding
&& is_system_conf
)
925 keybinding
->flags
|= KBDB_DEFAULT_KEY
| KBDB_DEFAULT_BINDING
;
931 bind_act(unsigned char *keymap_str
, const unsigned char *keystroke_str
)
933 enum keymap_id keymap_id
;
934 unsigned char *action
;
935 struct keybinding
*keybinding
;
937 keymap_id
= get_keymap_id(keymap_str
);
938 if (keymap_id
== KEYMAP_INVALID
)
941 keybinding
= kbd_stroke_lookup(keymap_id
, keystroke_str
);
942 if (!keybinding
) return NULL
;
944 action
= get_action_name(keymap_id
, keybinding
->action_id
);
948 keybinding
->flags
|= KBDB_WATERMARK
;
949 return straconcat("\"", action
, "\"", (unsigned char *) NULL
);
953 single_bind_config_string(struct string
*file
, enum keymap_id keymap_id
,
954 struct keybinding
*keybinding
)
956 unsigned char *keymap_str
= get_keymap_name(keymap_id
);
957 unsigned char *action_str
= get_action_name(keymap_id
, keybinding
->action_id
);
959 if (!keymap_str
|| !action_str
|| action_str
[0] == ' ')
962 if (keybinding
->flags
& KBDB_WATERMARK
) {
963 keybinding
->flags
&= ~KBDB_WATERMARK
;
967 /* TODO: Maybe we should use string.write.. */
968 add_to_string(file
, "bind \"");
969 add_to_string(file
, keymap_str
);
970 add_to_string(file
, "\" \"");
971 add_keystroke_to_string(file
, &keybinding
->kbd
, 1);
972 add_to_string(file
, "\" = \"");
973 add_to_string(file
, action_str
);
974 add_char_to_string(file
, '\"');
975 add_char_to_string(file
, '\n');
979 bind_config_string(struct string
*file
)
981 enum keymap_id keymap_id
;
983 for (keymap_id
= 0; keymap_id
< KEYMAP_MAX
; keymap_id
++) {
984 struct keybinding
*keybinding
;
986 foreach (keybinding
, keymaps
[keymap_id
]) {
987 /* Don't save default keybindings that has not been
988 * deleted (rebound to none action) (Bug 337). */
989 if (keybinding
->flags
& KBDB_DEFAULT_BINDING
)
992 single_bind_config_string(file
, keymap_id
, keybinding
);
997 struct module kbdbind_module
= struct_module(
998 /* Because this module is listed in main_modules rather than
999 * in builtin_modules, its name does not appear in the user
1000 * interface and so need not be translatable. */
1001 /* name: */ "Keyboard Bindings",
1002 /* options: */ NULL
,
1004 /* submodules: */ NULL
,
1006 /* init: */ init_keymaps
,
1007 /* done: */ free_keymaps