2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
12 #include <lib-ui/lib-ui.h>
13 #include <lib-ui/menu.h>
19 #define MUTT_FUNCTIONS_VALUES
20 #include "functions.def"
22 struct mapping_t Menus[] = {
23 {"alias", MENU_ALIAS},
24 {"attach", MENU_ATTACH},
25 {"browser", MENU_FOLDER},
26 {"compose", MENU_COMPOSE},
27 {"editor", MENU_EDITOR},
29 {"pager", MENU_PAGER},
30 {"postpone", MENU_POST},
32 {"smime", MENU_SMIME},
33 {"key_select_pgp", MENU_KEY_SELECT_PGP},
34 {"key_select_smime", MENU_KEY_SELECT_SMIME},
35 {"query", MENU_QUERY},
36 {"generic", MENU_GENERIC},
40 static struct mapping_t KeyNames[] = {
41 {"<PageUp>", KEY_PPAGE},
42 {"<PageDown>", KEY_NPAGE},
45 {"<Right>", KEY_RIGHT},
48 {"<BackSpace>", KEY_BACKSPACE},
53 {"<Enter>", KEY_ENTER},
60 {"<BackTab>", KEY_BTAB},
68 /* contains the last key the user pressed */
71 struct keymap_t *Keymaps[MENU_MAX];
73 static struct keymap_t *allocKeys(int len, keycode_t *keys)
77 p = p_new(struct keymap_t, 1);
79 p->keys = p_dup(keys, len);
83 static int parse_fkey (const char *s)
87 if (s[0] != '<' || ascii_tolower(s[1]) != 'f')
90 n = strtol(s + 2, (char **)&s, 10);
91 return *s == '>' ? n : -1;
95 * This function parses the string <NNN> and uses the octal value as the key
98 static int parse_keycode (const char *s)
104 n = strtol(s + 1, (char **)&s, 8);
105 return *s == '>' ? n : -1;
108 static int parsekeys (const char *str, keycode_t * d, int max)
115 m_strcpy(buff, sizeof(buff), str);
120 if (*s == '<' && (t = strchr (s, '>'))) {
125 if ((n = mutt_getvaluebyname (s, KeyNames)) != -1) {
129 else if ((n = parse_fkey (s)) > 0) {
133 else if ((n = parse_keycode (s)) > 0) {
142 *d = (unsigned char) *s;
152 /* insert a key sequence into the specified map. the map is sorted by ASCII
153 * value (lowest to highest)
155 void km_bind (const char *s, int menu, int op, char *macro, char *descr)
157 struct keymap_t *map, *tmp, *last = NULL, *next;
158 keycode_t buf[MAX_SEQ];
159 int len, pos = 0, lastpos = 0;
161 len = parsekeys (s, buf, MAX_SEQ);
163 map = allocKeys (len, buf);
165 map->macro = m_strdup(macro);
166 map->descr = m_strdup(descr);
171 if (pos >= len || pos >= tmp->len) {
172 /* map and tmp match, but have different lengths, so overwrite */
176 p_delete(&tmp->macro);
177 p_delete(&tmp->keys);
178 p_delete(&tmp->descr);
182 while (tmp && len >= pos);
186 else if (buf[pos] == tmp->keys[pos])
188 else if (buf[pos] < tmp->keys[pos]) {
189 /* found location to insert between last and tmp */
193 else { /* buf[pos] > tmp->keys[pos] */
212 void km_bindkey (const char *s, int menu, int op)
214 km_bind (s, menu, op, NULL, NULL);
217 static int get_op (struct binding_t *bindings, const char *start, ssize_t len)
221 for (i = 0; bindings[i].name; i++) {
222 if (!ascii_strncasecmp (start, bindings[i].name, len) &&
223 m_strlen(bindings[i].name) == len)
224 return bindings[i].op;
230 static const char *get_func (struct binding_t *bindings, int op)
234 for (i = 0; bindings[i].name; i++) {
235 if (bindings[i].op == op) {
236 return bindings[i].name;
243 static void push_string(const char *s)
245 const char *pp, *p = s + m_strlen(s) - 1;
250 /* if we see something like "<PageUp>", look to see if it is a real
251 function name and return the corresponding value */
253 for (pp = p - 1; pp >= s && *pp != '<'; pp--);
255 if ((i = parse_fkey (pp)) > 0) {
256 mutt_ungetch (KEY_F (i), 0);
262 for (i = 0; KeyNames[i].name; i++) {
263 if (!ascii_strncasecmp (pp, KeyNames[i].name, l))
266 if (KeyNames[i].name) {
268 mutt_ungetch (KeyNames[i].value, 0);
273 /* See if it is a valid command
274 * skip the '<' and the '>' when comparing */
275 for (i = 0; Menus[i].name; i++) {
276 struct binding_t *binding = km_get_table (Menus[i].value);
279 op = get_op (binding, pp + 1, l - 2);
286 mutt_ungetch (0, op);
292 mutt_ungetch ((unsigned char) *p--, 0);
296 static int retry_generic (int menu, keycode_t * keys, int keyslen,
299 if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER) {
301 mutt_ungetch (lastkey, 0);
302 for (; keyslen; keyslen--)
303 mutt_ungetch (keys[keyslen - 1], 0);
304 return km_dokey (MENU_GENERIC);
306 if (menu != MENU_EDITOR) {
307 /* probably a good idea to flush input here so we can abort macros */
314 * >0 function to execute
315 * OP_NULL no function bound to key sequence
316 * -1 error occured while reading input
318 int km_dokey (int menu)
321 struct keymap_t *map = Keymaps[menu];
328 return retry_generic (menu, NULL, 0, 0);
331 /* ncurses doesn't return on resized screen when timeout is set to zero */
332 if (menu != MENU_EDITOR)
333 wtimeout (main_w, (Timeout > 0 ? Timeout : 60) * 1000);
337 if (menu != MENU_EDITOR)
338 wtimeout (main_w, -1); /* restore blocking operation */
344 /* do we have an op already? */
346 const char *func = NULL;
347 struct binding_t *bindings;
349 /* is this a valid op for this menu? */
350 if ((bindings = km_get_table (menu)) &&
351 (func = get_func (bindings, tmp.op)))
354 if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op))
357 if (menu != MENU_EDITOR && menu != MENU_PAGER) {
358 /* check generic menu */
359 bindings = OpGeneric;
360 if ((func = get_func (bindings, tmp.op)))
364 /* Sigh. Valid function but not in this context.
365 * Find the literal string and push it back */
366 for (i = 0; Menus[i].name; i++) {
367 bindings = km_get_table (Menus[i].value);
369 func = get_func (bindings, tmp.op);
371 /* careful not to feed the <..> as one token. otherwise
372 * push_string() will push the bogus op right back! */
373 mutt_ungetch ('>', 0);
375 mutt_ungetch ('<', 0);
380 /* continue to chew */
385 /* Nope. Business as usual */
386 while (LastKey > map->keys[pos]) {
387 if (pos > map->eq || !map->next)
388 return retry_generic (menu, map->keys, pos, LastKey);
392 if (LastKey != map->keys[pos])
393 return retry_generic (menu, map->keys, pos, LastKey);
395 if (++pos == map->len) {
397 if (map->op != OP_MACRO)
402 mutt_error _("Macro loop detected.");
407 push_string (map->macro);
416 static void create_bindings (struct binding_t *map, int menu)
420 for (i = 0; map[i].name; i++)
422 km_bindkey (map[i].seq, menu, map[i].op);
425 const char *km_keyname(int c)
430 if ((p = mutt_getnamebyvalue (c, KeyNames)))
433 if (c < 256 && c > -128 && iscntrl ((unsigned char) c)) {
439 buf[1] = (c + '@') & 0x7f;
443 snprintf (buf, sizeof (buf), "\\%d%d%d", c >> 6, (c >> 3) & 7, c & 7);
445 else if (c >= KEY_F0 && c < KEY_F (256)) /* this maximum is just a guess */
446 sprintf (buf, "<F%d>", c - KEY_F0);
447 else if (isprint((unsigned char)c))
448 snprintf (buf, sizeof (buf), "%c", (unsigned char) c);
450 snprintf (buf, sizeof (buf), "\\x%hx", (unsigned short) c);
454 int km_expand_key (char *s, size_t len, struct keymap_t *map)
463 m_strcpy(s, len, km_keyname(map->keys[p]));
464 len -= (l = m_strlen(s));
466 if (++p >= map->len || !len)
475 struct keymap_t *km_find_func (int menu, int func)
477 struct keymap_t *map = Keymaps[menu];
479 for (; map; map = map->next)
487 p_clear(Keymaps, MENU_MAX);
489 create_bindings (OpAttach, MENU_ATTACH);
490 create_bindings (OpBrowser, MENU_FOLDER);
491 create_bindings (OpCompose, MENU_COMPOSE);
492 create_bindings (OpMain, MENU_MAIN);
493 create_bindings (OpPager, MENU_PAGER);
494 create_bindings (OpPost, MENU_POST);
495 create_bindings (OpQuery, MENU_QUERY);
496 create_bindings (OpAlias, MENU_ALIAS);
497 create_bindings (OpPgp, MENU_PGP);
498 create_bindings (OpSmime, MENU_SMIME);
499 create_bindings (OpPgp, MENU_KEY_SELECT_PGP);
500 create_bindings (OpSmime, MENU_KEY_SELECT_SMIME);
502 /* bindings for the line editor */
503 create_bindings (OpEditor, MENU_EDITOR);
505 km_bindkey ("<up>", MENU_EDITOR, OP_EDITOR_HISTORY_UP);
506 km_bindkey ("<down>", MENU_EDITOR, OP_EDITOR_HISTORY_DOWN);
507 km_bindkey ("<left>", MENU_EDITOR, OP_EDITOR_BACKWARD_CHAR);
508 km_bindkey ("<right>", MENU_EDITOR, OP_EDITOR_FORWARD_CHAR);
509 km_bindkey ("<home>", MENU_EDITOR, OP_EDITOR_BOL);
510 km_bindkey ("<end>", MENU_EDITOR, OP_EDITOR_EOL);
511 km_bindkey ("<backspace>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
512 km_bindkey ("<delete>", MENU_EDITOR, OP_EDITOR_BACKSPACE);
513 km_bindkey ("\177", MENU_EDITOR, OP_EDITOR_BACKSPACE);
515 /* generic menu keymap */
516 create_bindings (OpGeneric, MENU_GENERIC);
518 km_bindkey ("<home>", MENU_GENERIC, OP_FIRST_ENTRY);
519 km_bindkey ("<end>", MENU_GENERIC, OP_LAST_ENTRY);
520 km_bindkey ("<pagedown>", MENU_GENERIC, OP_NEXT_PAGE);
521 km_bindkey ("<pageup>", MENU_GENERIC, OP_PREV_PAGE);
522 km_bindkey ("<right>", MENU_GENERIC, OP_NEXT_PAGE);
523 km_bindkey ("<left>", MENU_GENERIC, OP_PREV_PAGE);
524 km_bindkey ("<up>", MENU_GENERIC, OP_PREV_ENTRY);
525 km_bindkey ("<down>", MENU_GENERIC, OP_NEXT_ENTRY);
526 km_bindkey ("1", MENU_GENERIC, OP_JUMP);
527 km_bindkey ("2", MENU_GENERIC, OP_JUMP);
528 km_bindkey ("3", MENU_GENERIC, OP_JUMP);
529 km_bindkey ("4", MENU_GENERIC, OP_JUMP);
530 km_bindkey ("5", MENU_GENERIC, OP_JUMP);
531 km_bindkey ("6", MENU_GENERIC, OP_JUMP);
532 km_bindkey ("7", MENU_GENERIC, OP_JUMP);
533 km_bindkey ("8", MENU_GENERIC, OP_JUMP);
534 km_bindkey ("9", MENU_GENERIC, OP_JUMP);
536 km_bindkey ("<enter>", MENU_GENERIC, OP_GENERIC_SELECT_ENTRY);
538 /* Miscellaneous extra bindings */
540 km_bindkey (" ", MENU_MAIN, OP_DISPLAY_MESSAGE);
541 km_bindkey ("<up>", MENU_MAIN, OP_MAIN_PREV_UNDELETED);
542 km_bindkey ("<down>", MENU_MAIN, OP_MAIN_NEXT_UNDELETED);
543 km_bindkey ("J", MENU_MAIN, OP_NEXT_ENTRY);
544 km_bindkey ("K", MENU_MAIN, OP_PREV_ENTRY);
545 km_bindkey ("x", MENU_MAIN, OP_EXIT);
547 km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
549 km_bindkey ("x", MENU_PAGER, OP_EXIT);
550 km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
551 km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
552 km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
553 km_bindkey ("<up>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
554 km_bindkey ("<right>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
555 km_bindkey ("<down>", MENU_PAGER, OP_MAIN_NEXT_UNDELETED);
556 km_bindkey ("<left>", MENU_PAGER, OP_MAIN_PREV_UNDELETED);
557 km_bindkey ("<home>", MENU_PAGER, OP_PAGER_TOP);
558 km_bindkey ("<end>", MENU_PAGER, OP_PAGER_BOTTOM);
559 km_bindkey ("1", MENU_PAGER, OP_JUMP);
560 km_bindkey ("2", MENU_PAGER, OP_JUMP);
561 km_bindkey ("3", MENU_PAGER, OP_JUMP);
562 km_bindkey ("4", MENU_PAGER, OP_JUMP);
563 km_bindkey ("5", MENU_PAGER, OP_JUMP);
564 km_bindkey ("6", MENU_PAGER, OP_JUMP);
565 km_bindkey ("7", MENU_PAGER, OP_JUMP);
566 km_bindkey ("8", MENU_PAGER, OP_JUMP);
567 km_bindkey ("9", MENU_PAGER, OP_JUMP);
569 km_bindkey ("<enter>", MENU_PAGER, OP_NEXT_LINE);
571 km_bindkey ("<return>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
572 km_bindkey ("<enter>", MENU_ALIAS, OP_GENERIC_SELECT_ENTRY);
573 km_bindkey ("<space>", MENU_ALIAS, OP_TAG);
575 km_bindkey ("<enter>", MENU_ATTACH, OP_VIEW_ATTACH);
576 km_bindkey ("<enter>", MENU_COMPOSE, OP_VIEW_ATTACH);
578 /* edit-to (default "t") hides generic tag-entry in Compose menu
579 This will bind tag-entry to "T" in the Compose menu */
580 km_bindkey ("T", MENU_COMPOSE, OP_TAG);
583 void km_error_key (int menu)
586 struct keymap_t *key;
588 if (!(key = km_find_func (menu, OP_HELP)))
589 key = km_find_func (MENU_GENERIC, OP_HELP);
591 if (!(km_expand_key (buf, sizeof (buf), key))) {
592 mutt_error _("Key is not bound.");
597 /* make sure the key is really the help key in this menu */
599 if (km_dokey (menu) != OP_HELP) {
600 mutt_error _("Key is not bound.");
605 mutt_error (_("Key is not bound. Press '%s' for help."), buf);
609 int mutt_parse_push (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
614 mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
616 m_strcpy(err->data, err->dsize, _("push: too many arguments"));
620 push_string (buf->data);
624 /* expects to see: <menu-string>,<menu-string>,... <key-string> */
625 static char *parse_keymap (int *menu, BUFFER * s, int maxmenus, int *nummenus,
635 mutt_extract_token (&buf, s, 0);
638 while (i < maxmenus) {
643 if ((menu[i] = mutt_getvaluebyname(p, Menus)) == -1) {
644 snprintf (err->data, err->dsize, _("%s: no such menu"), p);
655 mutt_extract_token (&buf, s, 0);
658 m_strcpy(err->data, err->dsize, _("null key sequence"));
660 else if (MoreArgs (s))
664 m_strcpy(err->data, err->dsize, _("too few arguments"));
672 try_bind (char *key, int menu, char *func, struct binding_t *bindings)
676 for (i = 0; bindings[i].name; i++)
677 if (m_strcmp(func, bindings[i].name) == 0) {
678 km_bindkey (key, menu, bindings[i].op);
684 struct binding_t *km_get_table (int menu)
709 case MENU_KEY_SELECT_PGP:
711 case MENU_KEY_SELECT_SMIME:
717 /* bind menu-name '<key_sequence>' function-name */
718 int mutt_parse_bind (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
721 struct binding_t *bindings = NULL;
723 int menu[countof(Menus) - 1], r =
726 if (!(key = parse_keymap(menu, s, countof(menu), &nummenus, err)))
729 /* function to execute */
730 mutt_extract_token (buf, s, 0);
732 m_strcpy(err->data, err->dsize, _("bind: too many arguments"));
735 else if (ascii_strcasecmp ("noop", buf->data) == 0) {
736 for (i = 0; i < nummenus; ++i) {
737 km_bindkey (key, menu[i], OP_NULL); /* the `unbind' command */
741 for (i = 0; i < nummenus; ++i) {
742 /* First check the "generic" list of commands */
743 if (menu[i] == MENU_PAGER || menu[i] == MENU_EDITOR ||
744 menu[i] == MENU_GENERIC ||
745 try_bind (key, menu[i], buf->data, OpGeneric) != 0) {
746 /* Now check the menu-specific list of commands (if they exist) */
747 bindings = km_get_table (menu[i]);
748 if (bindings && try_bind (key, menu[i], buf->data, bindings) != 0) {
749 snprintf (err->data, err->dsize, _("%s: no such function in map"),
760 /* macro <menu> <key> <macro> <description> */
761 int mutt_parse_macro (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
764 int menu[sizeof (Menus) / sizeof (struct mapping_t) - 1], r =
770 parse_keymap (menu, s, sizeof (menu) / sizeof (menu[0]), &nummenus,
774 mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
775 /* make sure the macro sequence is not an empty string */
777 m_strcpy(err->data, err->dsize, _("macro: empty key sequence"));
781 seq = m_strdup(buf->data);
782 mutt_extract_token (buf, s, M_TOKEN_CONDENSE);
785 m_strcpy(err->data, err->dsize, _("macro: too many arguments"));
788 for (i = 0; i < nummenus; ++i) {
789 km_bind (key, menu[i], OP_MACRO, seq, buf->data);
797 for (i = 0; i < nummenus; ++i) {
798 km_bind (key, menu[i], OP_MACRO, buf->data, NULL);
807 /* exec function-name */
808 int mutt_parse_exec (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
813 struct binding_t *bindings = NULL;
817 m_strcpy(err->data, err->dsize, _("exec: no arguments"));
822 mutt_extract_token (buf, s, 0);
823 function = buf->data;
825 if ((bindings = km_get_table (CurrentMenu)) == NULL
826 && CurrentMenu != MENU_PAGER)
827 bindings = OpGeneric;
829 ops[nops] = get_op (bindings, function, m_strlen(function));
830 if (ops[nops] == OP_NULL && CurrentMenu != MENU_PAGER)
831 ops[nops] = get_op (OpGeneric, function, m_strlen(function));
833 if (ops[nops] == OP_NULL) {
835 mutt_error (_("%s: no such function"), function);
840 while (MoreArgs (s) && nops < ssizeof (ops) / ssizeof (ops[0]));
843 mutt_ungetch (0, ops[--nops]);