diff --git a/src/bfu/dialog.c b/src/bfu/dialog.c index 289f4b66b..fad2d7dd9 100644 --- a/src/bfu/dialog.c +++ b/src/bfu/dialog.c @@ -285,16 +285,27 @@ select_button_by_flag(struct dialog_data *dlg_data, int flag) static void select_button_by_key(struct dialog_data *dlg_data) { - unsigned char key; + term_event_char_T key; +#ifdef CONFIG_UTF_8 + int codepage; +#endif + struct widget_data *widget_data; struct term_event *ev = dlg_data->term_event; if (!check_kbd_label_key(ev)) return; +#ifdef CONFIG_UTF_8 + key = unicode_fold_label_case(get_kbd_key(ev)); + codepage = get_opt_codepage_tree(dlg_data->win->term->spec, "charset"); +#else key = toupper(get_kbd_key(ev)); +#endif foreach_widget(dlg_data, widget_data) { int hk_pos; + unsigned char *hk_ptr; + term_event_char_T hk_char; if (widget_data->widget->type != WIDGET_BUTTON) continue; @@ -303,16 +314,23 @@ select_button_by_key(struct dialog_data *dlg_data) * one else we fallback to first character in button * name. */ hk_pos = widget_data->widget->info.button.hotkey_pos; - if (hk_pos >= 0) { - if (toupper(widget_data->widget->text[hk_pos + 1]) != key) - continue; - } else { - if (toupper(widget_data->widget->text[0]) != key) - continue; - } + if (hk_pos >= 0) + hk_ptr = &widget_data->widget->text[hk_pos + 1]; + else + hk_ptr = widget_data->widget->text; - select_dlg_item(dlg_data, widget_data); - break; +#ifdef CONFIG_UTF_8 + hk_char = cp_to_unicode(codepage, &hk_ptr, + strchr(hk_ptr, '\0')); + hk_char = unicode_fold_label_case(hk_char); +#else + hk_char = toupper(*hk_ptr); +#endif + + if (hk_char == key) { + select_dlg_item(dlg_data, widget_data); + break; + } } } diff --git a/src/bfu/hotkey.c b/src/bfu/hotkey.c index 0c3078dec..4feaac792 100644 --- a/src/bfu/hotkey.c +++ b/src/bfu/hotkey.c @@ -122,10 +122,15 @@ refresh_hotkeys(struct terminal *term, struct menu *menu) } static int -check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *term, +check_hotkeys_common(struct menu *menu, term_event_char_T hotkey, struct terminal *term, int check_mode) { +#ifdef CONFIG_UTF_8 + unicode_val_T key = unicode_fold_label_case(hotkey); + int codepage = get_opt_codepage_tree(term->spec, "charset"); +#else unsigned char key = toupper(hotkey); +#endif int i = menu->selected; int start; @@ -138,6 +143,9 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t do { struct menu_item *item; unsigned char *text; +#ifdef CONFIG_UTF_8 + unicode_val_T items_hotkey; +#endif int found; if (++i == menu->size) i = 0; @@ -150,6 +158,8 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t if (mi_text_translate(item)) text = _(text, term); if (!text || !*text) continue; + /* Change @text to point to the character that should + * be compared to @key. */ if (check_mode == 0) { /* Does the key (upcased) matches one of the * hotkeys in menu ? */ @@ -158,14 +168,22 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t #ifdef CONFIG_DEBUG if (key_pos < 0) key_pos = -key_pos; #endif - found = (key_pos && (toupper(text[key_pos]) == key)); - + if (!key_pos) continue; + text += key_pos; } else { /* Does the key (upcased) matches first letter * of menu item left text ? */ - found = (toupper(*text) == key); } + /* Compare @key to the character to which @text points. */ +#ifdef CONFIG_UTF_8 + items_hotkey = cp_to_unicode(codepage, &text, + strchr(text, '\0')); + found = (unicode_fold_label_case(items_hotkey) == key); +#else + found = (toupper(*text) == key); +#endif + if (found) { menu->selected = i; return 1; @@ -178,7 +196,7 @@ check_hotkeys_common(struct menu *menu, unsigned char hotkey, struct terminal *t /* Returns true if a hotkey was found in the menu, and set menu->selected. */ int -check_hotkeys(struct menu *menu, unsigned char key, struct terminal *term) +check_hotkeys(struct menu *menu, term_event_char_T key, struct terminal *term) { return check_hotkeys_common(menu, key, term, 0); } @@ -188,7 +206,7 @@ check_hotkeys(struct menu *menu, unsigned char key, struct terminal *term) * to selected entry. * It returns 1 if found and set menu->selected. */ int -check_not_so_hot_keys(struct menu *menu, unsigned char key, struct terminal *term) +check_not_so_hot_keys(struct menu *menu, term_event_char_T key, struct terminal *term) { return check_hotkeys_common(menu, key, term, 1); } diff --git a/src/bfu/hotkey.h b/src/bfu/hotkey.h index 8ddca989a..24a8352c1 100644 --- a/src/bfu/hotkey.h +++ b/src/bfu/hotkey.h @@ -3,6 +3,8 @@ #ifndef EL__BFU_HOTKEY_H #define EL__BFU_HOTKEY_H +#include "terminal/kbd.h" + struct menu; struct terminal; @@ -13,7 +15,7 @@ void clear_hotkeys_cache(struct menu *menu); #endif void refresh_hotkeys(struct terminal *term, struct menu *menu); /* int is_hotkey(struct menu_item *item, unsigned char key, struct terminal *term); */ -int check_hotkeys(struct menu *menu, unsigned char hotkey, struct terminal *term); -int check_not_so_hot_keys(struct menu *menu, unsigned char key, struct terminal *term); +int check_hotkeys(struct menu *menu, term_event_char_T hotkey, struct terminal *term); +int check_not_so_hot_keys(struct menu *menu, term_event_char_T key, struct terminal *term); #endif diff --git a/src/intl/charsets.c b/src/intl/charsets.c index 8fd19f9b7..1ece95029 100644 --- a/src/intl/charsets.c +++ b/src/intl/charsets.c @@ -408,8 +408,7 @@ unicode_to_cell(unicode_val_T c) } /* Fold the case of a Unicode character, so that hotkeys in labels can - * be compared case-insensitively. This should be called only if - * check_kbd_label_key(c) is true. It is unspecified whether the + * be compared case-insensitively. It is unspecified whether the * result will be in upper or lower case. */ unicode_val_T unicode_fold_label_case(unicode_val_T c) @@ -522,6 +521,25 @@ cp2utf_8(int from, int c) return encode_utf_8(cp2u_shared(&codepages[from], c)); } +#ifdef CONFIG_UTF_8 +unicode_val_T +cp_to_unicode(int codepage, unsigned char **string, unsigned char *end) +{ + if (is_cp_utf8(codepage)) + return utf_8_to_unicode(string, end); + else { + if (*string >= end) + return UCS_NO_CHAR; + else { + unicode_val_T ret = cp2u(codepage, **string); + ++*string; + return ret; + } + } +} +#endif /* CONFIG_UTF_8 */ + + static void add_utf_8(struct conv_table *ct, unicode_val_T u, unsigned char *str) { diff --git a/src/intl/charsets.h b/src/intl/charsets.h index 16bc8664d..6a28c8ae3 100644 --- a/src/intl/charsets.h +++ b/src/intl/charsets.h @@ -65,6 +65,7 @@ inline int unicode_to_cell(unicode_val_T); unicode_val_T unicode_fold_label_case(unicode_val_T); inline int strlen_utf8(unsigned char **); inline unicode_val_T utf_8_to_unicode(unsigned char **, unsigned char *); +unicode_val_T cp_to_unicode(int, unsigned char **, unsigned char *); #endif /* CONFIG_UTF_8 */ unicode_val_T cp2u(int, unsigned char); diff --git a/src/terminal/event.h b/src/terminal/event.h index 152b4567a..8599b55c7 100644 --- a/src/terminal/event.h +++ b/src/terminal/event.h @@ -170,17 +170,7 @@ void in_term(struct terminal *); #define check_kbd_modifier(event, mod) (kbd_modifier_is(&(event)->info.keyboard, (mod))) #define check_kbd_textinput_key(event) (get_kbd_key(event) >= ' ' && check_kbd_modifier(event, KBD_MOD_NONE)) -#ifdef CONFIG_UTF_8 -/* We must currently limit hotkeys of labels to ASCII, because - * get_kbd_key(event) is in UCS-4 and various event handlers pass it - * to toupper() if check_kbd_label_key() returns true. - * TO DO: Change the event handlers to use unicode_fold_label_case() - * instead. The code that extracts the hotkey from the label string - * will also have to be changed. */ -#define check_kbd_label_key(event) (get_kbd_key(event) > ' ' && get_kbd_key(event) <= 0x7F && (check_kbd_modifier(event, KBD_MOD_NONE) || check_kbd_modifier(event, KBD_MOD_ALT))) -#else /* !CONFIG_UTF_8 */ #define check_kbd_label_key(event) (get_kbd_key(event) > ' ' && (check_kbd_modifier(event, KBD_MOD_NONE) || check_kbd_modifier(event, KBD_MOD_ALT))) -#endif /* !CONFIG_UTF_8 */ /* For mouse events handling */ diff --git a/src/terminal/kbd.h b/src/terminal/kbd.h index 33fae62c7..b369155ab 100644 --- a/src/terminal/kbd.h +++ b/src/terminal/kbd.h @@ -1,13 +1,25 @@ #ifndef EL__TERMINAL_KBD_H #define EL__TERMINAL_KBD_H +#include "intl/charsets.h" + struct itrm; -/* Values <= -0x100 are special; from enum term_event_special_key. +/* A character received from a terminal. */ +#ifdef CONFIG_UTF_8 +typedef unicode_val_T term_event_char_T; /* in UCS-4 */ +#else +typedef unsigned char term_event_char_T; /* in the charset of the terminal */ +#endif + +/* A key received from a terminal, without modifiers. The value is + * either from term_event_char_T or from enum term_event_special_key. + * To check which one it is, use is_kbd_character(). + * + * Values <= -0x100 are special; from enum term_event_special_key. * Values between -0xFF and -2 are not used yet; treat as special. * Value == -1 is KBD_UNDEF; not sent via socket. - * Values >= 0 are characters received from the terminal; - * in UCS-4 #ifdef CONFIG_UTF_8. Test with is_kbd_character(). + * Values >= 0 are characters; from term_event_char_T. * * Any at least 32-bit signed integer type would work here; using an * exact-width type hurts portability in principle, but some other @@ -23,13 +35,24 @@ typedef enum { KBD_MOD_ALT = 4 } term_event_modifier_T; +/* A key received from a terminal, with modifiers. */ struct term_event_keyboard { term_event_key_T key; term_event_modifier_T modifier; }; +/* Like struct term_event_keyboard but used in the interlink protocol + * between ELinks processes. Because the processes may be running + * different versions of ELinks, especially if a new version has just + * been installed, this structure should be kept binary compatible as + * long as possible. See bug 793 for a list of pending changes to the + * protocol. */ struct interlink_event_keyboard { - /* Values <= -2 are not used, for ELinks 0.11 compatibility. + /* This is like term_event_key_T but carries individual bytes + * rather than entire characters, and uses different values + * for special keys. + * + * Values <= -2 are not used, for ELinks 0.11 compatibility. * Value == -1 is KBD_UNDEF; not sent via socket. * Values between 0 and 0xFF are bytes received from the terminal. * Values >= 0x100 are special; absolute values of constants @@ -84,9 +107,10 @@ static inline int is_kbd_fkey(term_event_key_T key) { return key <= KBD_F1 && ke #define kbd_fkey_to_number(key) (KBD_F1 - (key) + 1) /* int is_kbd_character(term_event_key_T key); - * Return true if @key is a character in some charset, rather than a - * special key. The character is not necessarily printable. As for - * which charset it is in, see the definition of term_event_key_T. */ + * Check whether @key is a character or a special key. + * Return true if @key is a character from term_event_char_T. + * (The character is not necessarily printable.) + * Return false if @key is a special key from enum term_event_special_key. */ #define is_kbd_character(key) ((key) >= 0) void