📄 keymap.c
字号:
/* * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H# include "config.h"#endif#include "mutt.h"#include "mutt_menu.h"#include "mutt_curses.h"#include "keymap.h"#include "mapping.h"#include "mutt_crypt.h"#include <stdlib.h>#include <string.h>#include <ctype.h>#include "functions.h"struct mapping_t Menus[] = { { "alias", MENU_ALIAS }, { "attach", MENU_ATTACH }, { "browser", MENU_FOLDER }, { "compose", MENU_COMPOSE }, { "editor", MENU_EDITOR }, { "index", MENU_MAIN }, { "pager", MENU_PAGER }, { "postpone", MENU_POST }, { "pgp", MENU_PGP }, { "smime", MENU_SMIME },#ifdef HAVE_GPGME { "key_select_pgp", MENU_KEY_SELECT_PGP }, { "key_select_smime", MENU_KEY_SELECT_SMIME },#endif#ifdef MIXMASTER { "mix", MENU_MIX },#endif { "query", MENU_QUERY }, { "generic", MENU_GENERIC }, { NULL, 0 }};#define mutt_check_menu(s) mutt_getvaluebyname(s, Menus)static struct mapping_t KeyNames[] = { { "<PageUp>", KEY_PPAGE }, { "<PageDown>", KEY_NPAGE }, { "<Up>", KEY_UP }, { "<Down>", KEY_DOWN }, { "<Right>", KEY_RIGHT }, { "<Left>", KEY_LEFT }, { "<Delete>", KEY_DC }, { "<BackSpace>",KEY_BACKSPACE }, { "<Insert>", KEY_IC }, { "<Home>", KEY_HOME }, { "<End>", KEY_END },#ifdef KEY_ENTER { "<Enter>", KEY_ENTER },#endif { "<Return>", M_ENTER_C }, { "<Esc>", '\033' }, { "<Tab>", '\t' }, { "<Space>", ' ' },#ifdef KEY_BTAB { "<BackTab>", KEY_BTAB },#endif#ifdef KEY_NEXT { "<Next>", KEY_NEXT },#endif { NULL, 0 }};/* contains the last key the user pressed */int LastKey;struct keymap_t *Keymaps[MENU_MAX];static struct keymap_t *allocKeys (int len, keycode_t *keys){ struct keymap_t *p; p = safe_calloc (1, sizeof (struct keymap_t)); p->len = len; p->keys = safe_malloc (len * sizeof (keycode_t)); memcpy (p->keys, keys, len * sizeof (keycode_t)); return (p);}static int parse_fkey(char *s){ char *t; int n = 0; if(s[0] != '<' || ascii_tolower(s[1]) != 'f') return -1; for(t = s + 2; *t && isdigit((unsigned char) *t); t++) { n *= 10; n += *t - '0'; } if(*t != '>') return -1; else return n;}/* * This function parses the string <NNN> and uses the octal value as the key * to bind. */static int parse_keycode (const char *s){ if (isdigit ((unsigned char) s[1]) && isdigit ((unsigned char) s[2]) && isdigit ((unsigned char) s[3]) && s[4] == '>') { return (s[3] - '0') + (s[2] - '0') * 8 + (s[1] - '0') * 64; } return -1;}static int parsekeys (char *str, keycode_t *d, int max){ int n, len = max; char buff[SHORT_STRING]; char c; char *s, *t; strfcpy(buff, str, sizeof(buff)); s = buff; while (*s && len) { *d = '\0'; if(*s == '<' && (t = strchr(s, '>'))) { t++; c = *t; *t = '\0'; if ((n = mutt_getvaluebyname (s, KeyNames)) != -1) { s = t; *d = n; } else if ((n = parse_fkey(s)) > 0) { s = t; *d = KEY_F (n); } else if ((n = parse_keycode(s)) > 0) { s = t; *d = n; } *t = c; } if(!*d) { *d = (unsigned char)*s; s++; } d++; len--; } return (max - len);}/* insert a key sequence into the specified map. the map is sorted by ASCII * value (lowest to highest) */void km_bind (char *s, int menu, int op, char *macro, char *descr){ struct keymap_t *map, *tmp, *last = NULL, *next; keycode_t buf[MAX_SEQ]; int len, pos = 0, lastpos = 0; len = parsekeys (s, buf, MAX_SEQ); map = allocKeys (len, buf); map->op = op; map->macro = safe_strdup (macro); map->descr = safe_strdup (descr); tmp = Keymaps[menu]; while (tmp) { if (pos >= len || pos >= tmp->len) { /* map and tmp match, but have different lengths, so overwrite */ do { len = tmp->eq; next = tmp->next; FREE (&tmp->macro); FREE (&tmp->keys); FREE (&tmp->descr); FREE (&tmp); tmp = next; } while (tmp && len >= pos); map->eq = len; break; } else if (buf[pos] == tmp->keys[pos]) pos++; else if (buf[pos] < tmp->keys[pos]) { /* found location to insert between last and tmp */ map->eq = pos; break; } else /* buf[pos] > tmp->keys[pos] */ { last = tmp; lastpos = pos; if (pos > tmp->eq) pos = tmp->eq; tmp = tmp->next; } } map->next = tmp; if (last) { last->next = map; last->eq = lastpos; } else Keymaps[menu] = map;}void km_bindkey (char *s, int menu, int op){ km_bind (s, menu, op, NULL, NULL);}static int get_op (struct binding_t *bindings, const char *start, size_t len){ int i; for (i = 0; bindings[i].name; i++) { if (!ascii_strncasecmp (start, bindings[i].name, len) && mutt_strlen (bindings[i].name) == len) return bindings[i].op; } return OP_NULL;}static char *get_func (struct binding_t *bindings, int op){ int i; for (i = 0; bindings[i].name; i++) { if (bindings[i].op == op) return bindings[i].name; } return NULL;}static void push_string (char *s){ char *pp, *p = s + mutt_strlen (s) - 1; size_t l; int i, op = OP_NULL; while (p >= s) { /* if we see something like "<PageUp>", look to see if it is a real function name and return the corresponding value */ if (*p == '>') { for (pp = p - 1; pp >= s && *pp != '<'; pp--) ; if (pp >= s) { if ((i = parse_fkey (pp)) > 0) { mutt_ungetch (KEY_F (i), 0); p = pp - 1; continue; } l = p - pp + 1; for (i = 0; KeyNames[i].name; i++) { if (!ascii_strncasecmp (pp, KeyNames[i].name, l)) break; } if (KeyNames[i].name) { /* found a match */ mutt_ungetch (KeyNames[i].value, 0); p = pp - 1; continue; } /* See if it is a valid command * skip the '<' and the '>' when comparing */ for (i = 0; Menus[i].name; i++) { struct binding_t *binding = km_get_table (Menus[i].value); if (binding) { op = get_op (binding, pp + 1, l - 2); if (op != OP_NULL) break; } } if (op != OP_NULL) { mutt_ungetch (0, op); p = pp - 1; continue; } } } mutt_ungetch ((unsigned char)*p--, 0); /* independent 8 bits chars */ }}static int retry_generic (int menu, keycode_t *keys, int keyslen, int lastkey){ if (menu != MENU_EDITOR && menu != MENU_GENERIC && menu != MENU_PAGER) { if (lastkey) mutt_ungetch (lastkey, 0); for (; keyslen; keyslen--) mutt_ungetch (keys[keyslen - 1], 0); return (km_dokey (MENU_GENERIC)); } if (menu != MENU_EDITOR) { /* probably a good idea to flush input here so we can abort macros */ mutt_flushinp (); } return OP_NULL;}/* return values: * >0 function to execute * OP_NULL no function bound to key sequence * -1 error occured while reading input */int km_dokey (int menu){ event_t tmp; struct keymap_t *map = Keymaps[menu]; int pos = 0; int n = 0; int i; if (!map) return (retry_generic (menu, NULL, 0, 0)); FOREVER { /* ncurses doesn't return on resized screen when timeout is set to zero */ if (menu != MENU_EDITOR) timeout ((Timeout > 0 ? Timeout : 60) * 1000); tmp = mutt_getch(); if (menu != MENU_EDITOR) timeout (-1); /* restore blocking operation */ LastKey = tmp.ch; if (LastKey == -1) return -1; /* do we have an op already? */ if (tmp.op) { char *func = NULL; struct binding_t *bindings; /* is this a valid op for this menu? */ if ((bindings = km_get_table (menu)) && (func = get_func (bindings, tmp.op))) return tmp.op; if (menu == MENU_EDITOR && get_func (OpEditor, tmp.op)) return tmp.op; if (menu != MENU_EDITOR && menu != MENU_PAGER) { /* check generic menu */ bindings = OpGeneric; if ((func = get_func (bindings, tmp.op))) return tmp.op; } /* Sigh. Valid function but not in this context. * Find the literal string and push it back */ for (i = 0; Menus[i].name; i++) { bindings = km_get_table (Menus[i].value); if (bindings) { func = get_func (bindings, tmp.op); if (func) { /* careful not to feed the <..> as one token. otherwise * push_string() will push the bogus op right back! */ mutt_ungetch ('>', 0); push_string (func); mutt_ungetch ('<', 0); break; } } } /* continue to chew */ if (func) continue; } /* Nope. Business as usual */ while (LastKey > map->keys[pos]) { if (pos > map->eq || !map->next) return (retry_generic (menu, map->keys, pos, LastKey)); map = map->next; } if (LastKey != map->keys[pos]) return (retry_generic (menu, map->keys, pos, LastKey)); if (++pos == map->len) { if (map->op != OP_MACRO) return map->op; if (n++ == 10) { mutt_flushinp (); mutt_error _("Macro loop detected."); return -1; } push_string (map->macro); map = Keymaps[menu]; pos = 0; } } /* not reached */}static void create_bindings (struct binding_t *map, int menu){ int i; for (i = 0 ; map[i].name ; i++) if (map[i].seq) km_bindkey (map[i].seq, menu, map[i].op);}char *km_keyname (int c){ static char buf[10];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -