diff options
author | Jonas Fonseca <jonas.fonseca@gmail.com> | 2014-03-02 22:36:29 -0500 |
---|---|---|
committer | Jonas Fonseca <jonas.fonseca@gmail.com> | 2014-03-06 19:49:58 -0500 |
commit | 237a269c8e14248ff903b50f7c6624f247626ea5 (patch) | |
tree | 1e0c0b6e9720ee052ffa08847b3c795e1c0afb3e | |
parent | b17610f27b56d7aa4c7af27e172b76e1ee1e99db (diff) |
Add multibyte support to struct key_input
Fixes #99
Closes #105
-rw-r--r-- | doc/tigrc.5.adoc | 11 | ||||
-rw-r--r-- | include/tig/keys.h | 14 | ||||
-rw-r--r-- | src/display.c | 74 | ||||
-rw-r--r-- | src/keys.c | 46 | ||||
-rw-r--r-- | src/tig.c | 2 |
5 files changed, 108 insertions, 39 deletions
diff --git a/doc/tigrc.5.adoc b/doc/tigrc.5.adoc index 39f85525..d05c10d3 100644 --- a/doc/tigrc.5.adoc +++ b/doc/tigrc.5.adoc @@ -335,6 +335,9 @@ bind status + !git commit --amend # User-defined internal command that reloads ~/.tigrc bind generic S :source ~/.tigrc + +# UTF8-encoded characters can be used as key values. +bind generic ΓΈ @sh -c "printf '%s' %(commit) | pbcopy" -------------------------------------------------------------------------- Or in the Git configuration files: @@ -362,10 +365,10 @@ to set key mapping in all keymaps. Key values:: -Key values should never be quoted. Use either the ASCII value or one of the -following symbolic key names. Symbolic key names are case insensitive, Use -*Hash* to bind to the `#` key, since the hash mark is used as a comment -character. +Key values should never be quoted. Use either an ASCII or UTF8-encoded character +or one of the following symbolic key names. Symbolic key names are case +insensitive. Use *Hash* to bind to the `#` key, since the hash mark is used as a +comment character. *Enter*, *Space*, *Backspace*, *Tab*, *Escape*, *Left*, *Right*, *Up*, *Down*, *Insert*, *Delete*, *Hash*, *Home*, *End*, *PageUp*, *PageDown*, *F1*, *F2*, *F3*, diff --git a/include/tig/keys.h b/include/tig/keys.h index 49ca2c65..77dcf50b 100644 --- a/include/tig/keys.h +++ b/include/tig/keys.h @@ -32,13 +32,25 @@ struct keymap { }; struct key_input { - int key; + union { + int key; + char bytes[7]; + } data; struct { bool escape:1; bool control:1; + bool multibytes:1; } modifiers; }; +static inline unsigned long +key_input_to_unicode(struct key_input *input) +{ + return input->modifiers.multibytes + ? utf8_to_unicode(input->data.bytes, strlen(input->data.bytes)) + : 0; +} + void add_keymap(struct keymap *keymap); struct keymap *get_keymap(const char *name, size_t namelen); struct keymap *get_keymaps(void); diff --git a/src/display.c b/src/display.c index 2464489b..f9265abd 100644 --- a/src/display.c +++ b/src/display.c @@ -435,24 +435,57 @@ get_input(int prompt_position, struct key_input *input, bool modifiers) redraw_display(TRUE); } else { + int pos, key_length; + input_mode = FALSE; if (key == erasechar()) key = KEY_BACKSPACE; - input->key = key; - return input->key; + + /* + * Ctrl-<key> values are represented using a 0x1F + * bitmask on the key value. To 'unmap' we assume that: + * + * - Ctrl-Z is handled by Ncurses. + * - Ctrl-m is the same as Return/Enter. + * - Ctrl-i is the same as Tab. + * + * For all other key values in the range the Ctrl flag + * is set and the key value is updated to the proper + * ASCII value. + */ + if (KEY_CTL('a') <= key && key <= KEY_CTL('x') && key != KEY_RETURN && key != KEY_TAB) { + input->modifiers.control = 1; + key = key | 0x40; + } + + if ((key >= KEY_MIN && key < KEY_MAX) || key < 0x1F) { // || key == ' ') { + input->data.key = key; + return input->data.key; + } + + input->modifiers.multibytes = 1; + input->data.bytes[0] = key; + + key_length = utf8_char_length(input->data.bytes); + for (pos = 1; pos < key_length && pos < sizeof(input->data.bytes) - 1; pos++) { + input->data.bytes[pos] = wgetch(status_win); + } + + return OK; } } } -typedef enum input_status (*input_handler)(void *data, char *buf, int c); +typedef enum input_status (*input_handler)(void *data, char *buf, struct key_input *input); static char * prompt_input(const char *prompt, input_handler handler, void *data) { enum input_status status = INPUT_OK; static char buf[SIZEOF_STR]; + unsigned char chars_length[SIZEOF_STR]; struct key_input input; - size_t pos = 0; + size_t pos = 0, chars = 0; buf[pos] = 0; @@ -468,10 +501,14 @@ prompt_input(const char *prompt, input_handler handler, void *data) break; case KEY_BACKSPACE: - if (pos > 0) - buf[--pos] = 0; - else + if (pos > 0) { + int len = chars_length[--chars]; + + pos -= len; + buf[pos] = 0; + } else { status = INPUT_CANCEL; + } break; case KEY_ESC: @@ -484,9 +521,14 @@ prompt_input(const char *prompt, input_handler handler, void *data) return NULL; } - status = handler(data, buf, input.key); - if (status == INPUT_OK) - buf[pos++] = (char) input.key; + status = handler(data, buf, &input); + if (status == INPUT_OK) { + int len = strlen(input.data.bytes); + + string_ncopy_do(buf + pos, sizeof(buf) - pos, input.data.bytes, len); + pos += len; + chars_length[chars++] = len; + } } } @@ -503,8 +545,10 @@ prompt_input(const char *prompt, input_handler handler, void *data) } static enum input_status -prompt_yesno_handler(void *data, char *buf, int c) +prompt_yesno_handler(void *data, char *buf, struct key_input *input) { + unsigned long c = key_input_to_unicode(input); + if (c == 'y' || c == 'Y') return INPUT_STOP; if (c == 'n' || c == 'N') @@ -524,9 +568,11 @@ prompt_yesno(const char *prompt) } static enum input_status -read_prompt_handler(void *data, char *buf, int c) +read_prompt_handler(void *data, char *buf, struct key_input *input) { - return isprint(c) ? INPUT_OK : INPUT_SKIP; + unsigned long c = key_input_to_unicode(input); + + return unicode_width(c, 8) ? INPUT_OK : INPUT_SKIP; } char * @@ -583,7 +629,7 @@ prompt_menu(const char *prompt, const struct menu_item *items, int *selected) default: for (i = 0; items[i].text; i++) - if (items[i].hotkey == input.key) { + if (items[i].hotkey == input.data.bytes[0]) { *selected = i; status = INPUT_STOP; break; @@ -90,7 +90,7 @@ get_keybinding(struct keymap *keymap, struct key_input *input) if (!memcmp(&generic_keymap.data[i].input, input, sizeof(*input))) return generic_keymap.data[i].request; - return (enum request) input->key; + return REQ_NONE; } @@ -141,7 +141,15 @@ get_key_value(const char *name, struct key_input *input) for (i = 0; i < ARRAY_SIZE(key_table); i++) if (!strcasecmp(key_table[i].name, name)) { - input->key = key_table[i].value; + if (key_table[i].value == ' ') { + name = " "; + break; + } + if (key_table[i].value == '#') { + name = "#"; + break; + } + input->data.key = key_table[i].value; return OK; } @@ -153,8 +161,10 @@ get_key_value(const char *name, struct key_input *input) name += 1; } - if (strlen(name) == 1 && isprint(*name)) { - input->key = *name; + i = utf8_char_length(name); + if (strlen(name) == i && utf8_to_unicode(name, i) != 0) { + strncpy(input->data.bytes, name, i); + input->modifiers.multibytes = 1; return OK; } @@ -164,25 +174,23 @@ get_key_value(const char *name, struct key_input *input) const char * get_key_name(const struct key_input *input) { + static char buf[SIZEOF_STR]; + const char *modifier = ""; int key; - for (key = 0; key < ARRAY_SIZE(key_table); key++) - if (key_table[key].value == input->key) - return key_table[key].name; - - if (input->key >= 0x20 && input->key < 0x7f) { - static char buf[SIZEOF_STR]; - const char *modifier = ""; - char key_char = input->key; + if (!input->modifiers.multibytes) { + for (key = 0; key < ARRAY_SIZE(key_table); key++) + if (key_table[key].value == input->data.key) + return key_table[key].name; + } - if (input->modifiers.escape) - modifier = "^["; - else if (input->modifiers.control) - modifier = "^"; + if (input->modifiers.escape) + modifier = "^["; + else if (input->modifiers.control) + modifier = "^"; - if (string_format(buf, "'%s%c'", modifier, key_char)) - return buf; - } + if (string_format(buf, "'%s%s'", modifier, input->data.bytes)) + return buf; return "(no key)"; } @@ -666,7 +666,7 @@ run_prompt_command(struct view *view, char *cmd) } } else if (cmd && strlen(cmd) == 1) { - struct key_input input = { cmd[0] }; + struct key_input input = { { cmd[0] } }; return get_keybinding(&view->ops->keymap, &input); |