iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / bfu / hotkey.c
blobc09b6ed809c387903e3ec47dd315babb044bb561
1 /* Hotkeys handling. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <string.h>
9 #include "elinks.h"
11 #include "bfu/hotkey.h"
12 #include "bfu/menu.h"
13 #include "config/kbdbind.h"
14 #include "intl/gettext/libintl.h"
15 #include "terminal/draw.h"
16 #include "terminal/terminal.h"
17 #include "terminal/window.h"
18 #include "util/conv.h"
19 #include "util/memory.h"
22 /* Return position (starting at 1) of the first tilde in text,
23 * or 0 if not found. */
24 static inline int
25 find_hotkey_pos(unsigned char *text)
27 if (text && *text) {
28 unsigned char *p = strchr(text, '~');
30 if (p) return (int) (p - text) + 1;
33 return 0;
36 void
37 init_hotkeys(struct terminal *term, struct menu *menu)
39 struct menu_item *mi;
41 #ifdef CONFIG_DEBUG
42 /* hotkey debugging */
43 if (menu->hotkeys) {
44 struct menu_item *used_hotkeys[255];
46 memset(used_hotkeys, 0, sizeof(used_hotkeys));
48 foreach_menu_item(mi, menu->items) {
49 unsigned char *text = mi->text;
51 if (!mi_has_left_text(mi)) continue;
52 if (mi_text_translate(mi)) text = _(text, term);
53 if (!*text) continue;
55 if (mi->hotkey_state != HKS_CACHED && !mi->hotkey_pos)
56 mi->hotkey_pos = find_hotkey_pos(text);
58 /* Negative value for hotkey_pos means the key is already
59 * used by another entry. We mark it to be able to highlight
60 * this hotkey in menus. --Zas */
61 if (mi->hotkey_pos) {
62 struct menu_item **used = &used_hotkeys[toupper(text[mi->hotkey_pos])];
64 if (*used) {
65 mi->hotkey_pos = -mi->hotkey_pos;
66 if ((*used)->hotkey_pos > 0)
67 (*used)->hotkey_pos = -(*used)->hotkey_pos;
70 *used = mi;
71 mi->hotkey_state = HKS_CACHED;
75 #endif
77 foreach_menu_item(mi, menu->items) {
78 if (!menu->hotkeys) {
79 mi->hotkey_pos = 0;
80 mi->hotkey_state = HKS_IGNORE;
81 } else if (mi->hotkey_state != HKS_CACHED
82 && !mi->hotkey_pos) {
83 unsigned char *text = mi->text;
85 if (!mi_has_left_text(mi)) continue;
86 if (mi_text_translate(mi)) text = _(text, term);
87 if (!*text) continue;
89 mi->hotkey_pos = find_hotkey_pos(text);
91 if (mi->hotkey_pos)
92 mi->hotkey_state = HKS_CACHED;
97 #ifdef CONFIG_NLS
98 void
99 clear_hotkeys_cache(struct menu *menu)
101 struct menu_item *item;
103 foreach_menu_item(item, menu->items) {
104 item->hotkey_state = menu->hotkeys ? HKS_SHOW : HKS_IGNORE;
105 item->hotkey_pos = 0;
108 #endif
110 void
111 refresh_hotkeys(struct terminal *term, struct menu *menu)
113 #ifdef CONFIG_NLS
114 if (current_language != menu->lang) {
115 clear_hotkeys_cache(menu);
116 init_hotkeys(term, menu);
117 menu->lang = current_language;
119 #else
120 init_hotkeys(term, menu);
121 #endif
124 static int
125 check_hotkeys_common(struct menu *menu, term_event_char_T hotkey, struct terminal *term,
126 int check_mode)
128 #ifdef CONFIG_UTF8
129 unicode_val_T key = unicode_fold_label_case(hotkey);
130 int codepage = get_terminal_codepage(term);
131 #else
132 unsigned char key = toupper(hotkey);
133 #endif
134 int i = menu->selected;
135 int start;
137 if (menu->size < 1) return 0;
139 i %= menu->size;
140 if (i < 0) i += menu->size;
142 start = i;
143 do {
144 struct menu_item *item;
145 unsigned char *text;
146 #ifdef CONFIG_UTF8
147 unicode_val_T items_hotkey;
148 #endif
149 int found;
151 if (++i == menu->size) i = 0;
153 item = &menu->items[i];
155 if (!mi_has_left_text(item)) continue;
157 text = item->text;
158 if (mi_text_translate(item)) text = _(text, term);
159 if (!text || !*text) continue;
161 /* Change @text to point to the character that should
162 * be compared to @key. */
163 if (check_mode == 0) {
164 /* Does the key (upcased) matches one of the
165 * hotkeys in menu ? */
166 int key_pos = item->hotkey_pos;
168 #ifdef CONFIG_DEBUG
169 if (key_pos < 0) key_pos = -key_pos;
170 #endif
171 if (!key_pos) continue;
172 text += key_pos;
173 } else {
174 /* Does the key (upcased) matches first letter
175 * of menu item left text ? */
178 /* Compare @key to the character to which @text points. */
179 #ifdef CONFIG_UTF8
180 items_hotkey = cp_to_unicode(codepage, &text,
181 strchr(text, '\0'));
182 /* items_hotkey can be UCS_NO_CHAR only if the text of
183 * the menu item is not in the expected codepage. */
184 assert(items_hotkey != UCS_NO_CHAR);
185 if_assert_failed continue;
186 found = (unicode_fold_label_case(items_hotkey) == key);
187 #else
188 found = (toupper(*text) == key);
189 #endif
191 if (found) {
192 menu->selected = i;
193 return 1;
196 } while (i != start);
198 return 0;
201 /* Returns true if a hotkey was found in the menu, and set menu->selected. */
203 check_hotkeys(struct menu *menu, term_event_char_T key, struct terminal *term)
205 return check_hotkeys_common(menu, key, term, 0);
208 /* Search if first letter of an entry in menu matches the key (caseless comp.).
209 * It searchs in all entries, from selected entry to bottom and then from top
210 * to selected entry.
211 * It returns 1 if found and set menu->selected. */
213 check_not_so_hot_keys(struct menu *menu, term_event_char_T key, struct terminal *term)
215 return check_hotkeys_common(menu, key, term, 1);