diff --git a/src/gui/gui-key.c b/src/gui/gui-key.c index 429421e97..911e86b5b 100644 --- a/src/gui/gui-key.c +++ b/src/gui/gui-key.c @@ -1236,6 +1236,125 @@ gui_key_is_safe (int context, const char *key) return 0; } +/* + * Checks if the key chunk seems valid. + * + * Example of valid key chunk: + * "meta-a" + * "meta-c" + * "meta-up" + * "ctrl-left" + * "ctrl-u" + * + * Example of raw codes or invalid keys: + * "meta-[A" (raw code) + * "ctrl-cb" (invalid: missing comma) + * + * Returns: + * 1: key chunk seems valid + * 0: key chunk seems either invalid or a raw code + */ + +int +gui_key_chunk_seems_valid (const char *chunk) +{ + int i, found, length; + + if (!chunk || !chunk[0]) + return 0; + + /* skip modifiers */ + found = 1; + while (found) + { + found = 0; + for (i = 0; gui_key_modifier_list[i]; i++) + { + length = strlen (gui_key_modifier_list[i]); + if (strncmp (chunk, gui_key_modifier_list[i], length) == 0) + { + chunk += length; + found = 1; + break; + } + } + } + + /* check if it's an alias */ + found = 0; + for (i = 0; gui_key_alias_list[i]; i++) + { + length = strlen (gui_key_alias_list[i]); + if (strncmp (chunk, gui_key_alias_list[i], length) == 0) + { + chunk += length; + found = 1; + break; + } + } + if (!found) + chunk = utf8_next_char (chunk); + + if (chunk[0]) + return 0; + + return 1; +} + +/* + * Checks if the key seems valid: not a raw code, and no comma is missing. + * + * Example of valid keys: + * "meta-a" + * "meta-c,b" + * "meta-w,meta-up" + * "ctrl-left" + * "ctrl-u" + * + * Example of raw codes or invalid keys: + * "meta-[A" (raw code) + * "ctrl-cb" (invalid: missing comma) + * + * Returns: + * 1: key seems valid + * 0: key seems either invalid or a raw code + */ + +int +gui_key_seems_valid (int context, const char *key) +{ + char **chunks; + int i, rc, chunks_count; + + if (!key || !key[0]) + return 0; + + /* "@" is allowed at beginning for cursor/mouse contexts */ + if ((key[0] == '@') + && ((context == GUI_KEY_CONTEXT_CURSOR) + || (context == GUI_KEY_CONTEXT_MOUSE))) + { + return 1; + } + + chunks = string_split (key, ",", NULL, 0, 0, &chunks_count); + if (!chunks) + return 0; + + rc = 1; + for (i = 0; i < chunks_count; i++) + { + if (!gui_key_chunk_seems_valid (chunks[i])) + { + rc = 0; + break; + } + } + + string_free_split (chunks); + return rc; +} + /* * Callback for changes on a key option. */ @@ -1575,6 +1694,15 @@ gui_key_bind (struct t_gui_buffer *buffer, int context, const char *key, } return NULL; } + if (!gui_key_seems_valid (context, key)) + { + gui_chat_printf ( + NULL, + _("%sWarning: key \"%s\" seems either a raw code or invalid, " + "it may not work (see /help key)"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + key); + } } gui_key_unbind (buffer, context, key); diff --git a/tests/unit/gui/test-gui-key.cpp b/tests/unit/gui/test-gui-key.cpp index f81cfd47d..5596279a4 100644 --- a/tests/unit/gui/test-gui-key.cpp +++ b/tests/unit/gui/test-gui-key.cpp @@ -35,6 +35,7 @@ extern int gui_key_get_current_context (); extern char *gui_key_legacy_internal_code (const char *key); extern char *gui_key_fix (const char *key); extern int gui_key_is_safe (int context, const char *key); +extern int gui_key_seems_valid (int context, const char *key); extern struct t_config_option *gui_key_new_option (int context, const char *name, const char *value); @@ -1034,6 +1035,44 @@ TEST(GuiKey, IsSafe) LONGS_EQUAL(1, gui_key_is_safe (GUI_KEY_CONTEXT_MOUSE, "@")); } +/* + * Tests functions: + * gui_key_chunk_seems_valid + * gui_key_seems_valid + */ + +TEST(GuiKey, SeemsValid) +{ + /* invalid: NULL or empty string */ + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, NULL)); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "")); + + /* raw codes: considered not valid */ + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-[A")); + + /* invalid keys: missing comma */ + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "ab")); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "@a")); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "homeZ")); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-cb")); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-updown")); + LONGS_EQUAL(0, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "@chat:button1")); + + /* valid keys */ + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "a")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "A")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "é")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "/")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-a")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-ctrl-a")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-c,b")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "meta-w,meta-up")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "ctrl-left")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_DEFAULT, "ctrl-u")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_CURSOR, "@chat:q")); + LONGS_EQUAL(1, gui_key_seems_valid (GUI_KEY_CONTEXT_MOUSE, "@chat:button1")); +} + /* * Tests functions: * gui_key_option_change_cb