Handle streams separately in tree_add_track()
[cmus.git] / keys.c
bloba9d6700e6d0385d7356dc761df8f9f0d77bc299c
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
4 * keys.[ch] by Frank Terbeck <ft@bewatermyfriend.org>
5 * heavily modified by Timo Hirvonen
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
23 #include "keys.h"
24 #include "help.h"
25 #include "ui_curses.h"
26 #include "command_mode.h"
27 #include "xmalloc.h"
29 #if defined(__sun__)
30 #include <ncurses.h>
31 #else
32 #include <curses.h>
33 #endif
35 const char * const key_context_names[NR_CTXS + 1] = {
36 "browser",
37 "common",
38 "filters",
39 "library",
40 "playlist",
41 "queue",
42 "settings",
43 NULL
46 struct binding *key_bindings[NR_CTXS] = { NULL, };
48 static const enum key_context view_to_context[] = {
49 CTX_LIBRARY,
50 CTX_LIBRARY,
51 CTX_PLAYLIST,
52 CTX_QUEUE,
53 CTX_BROWSER,
54 CTX_FILTERS,
55 CTX_SETTINGS,
58 #define KEY_IS_CHAR -255
60 /* key_table {{{
62 * key: KEY_IS_CHAR, not a key
63 * ch: 0, not a char
65 const struct key key_table[] = {
66 { "!", KEY_IS_CHAR, 33 },
67 { "\"", KEY_IS_CHAR, 34 },
68 { "#", KEY_IS_CHAR, 35 },
69 { "$", KEY_IS_CHAR, 36 },
70 { "%", KEY_IS_CHAR, 37 },
71 { "&", KEY_IS_CHAR, 38 },
72 { "'", KEY_IS_CHAR, 39 },
73 { "(", KEY_IS_CHAR, 40 },
74 { ")", KEY_IS_CHAR, 41 },
75 { "*", KEY_IS_CHAR, 42 },
76 { "+", KEY_IS_CHAR, 43 },
77 { ",", KEY_IS_CHAR, 44 },
78 { "-", KEY_IS_CHAR, 45 },
79 { ".", KEY_IS_CHAR, 46 },
80 { "0", KEY_IS_CHAR, 48 },
81 { "1", KEY_IS_CHAR, 49 },
82 { "2", KEY_IS_CHAR, 50 },
83 { "3", KEY_IS_CHAR, 51 },
84 { "4", KEY_IS_CHAR, 52 },
85 { "5", KEY_IS_CHAR, 53 },
86 { "6", KEY_IS_CHAR, 54 },
87 { "7", KEY_IS_CHAR, 55 },
88 { "8", KEY_IS_CHAR, 56 },
89 { "9", KEY_IS_CHAR, 57 },
90 { ";", KEY_IS_CHAR, 59 },
91 { "<", KEY_IS_CHAR, 60 },
92 { "=", KEY_IS_CHAR, 61 },
93 { ">", KEY_IS_CHAR, 62 },
94 { "@", KEY_IS_CHAR, 64 },
95 { "A", KEY_IS_CHAR, 65 },
96 { "B", KEY_IS_CHAR, 66 },
97 { "C", KEY_IS_CHAR, 67 },
98 { "D", KEY_IS_CHAR, 68 },
99 { "E", KEY_IS_CHAR, 69 },
100 { "F", KEY_IS_CHAR, 70 },
101 { "F1", KEY_F(1), 0 },
102 { "F10", KEY_F(10), 0 },
103 { "F11", KEY_F(11), 0 },
104 { "F12", KEY_F(12), 0 },
105 { "F2", KEY_F(2), 0 },
106 { "F3", KEY_F(3), 0 },
107 { "F4", KEY_F(4), 0 },
108 { "F5", KEY_F(5), 0 },
109 { "F6", KEY_F(6), 0 },
110 { "F7", KEY_F(7), 0 },
111 { "F8", KEY_F(8), 0 },
112 { "F9", KEY_F(9), 0 },
113 { "G", KEY_IS_CHAR, 71 },
114 { "H", KEY_IS_CHAR, 72 },
115 { "I", KEY_IS_CHAR, 73 },
116 { "J", KEY_IS_CHAR, 74 },
117 { "K", KEY_IS_CHAR, 75 },
118 { "KP_center", KEY_B2, 0 },
119 { "KP_lower_left", KEY_C1, 0 },
120 { "KP_lower_right", KEY_C3, 0 },
121 { "KP_upper_left", KEY_A1, 0 },
122 { "KP_upper_right", KEY_A3, 0 },
123 { "L", KEY_IS_CHAR, 76 },
124 { "M", KEY_IS_CHAR, 77 },
125 { "M-!", KEY_IS_CHAR, 161 },
126 { "M-\"", KEY_IS_CHAR, 162 },
127 { "M-#", KEY_IS_CHAR, 163 },
128 { "M-$", KEY_IS_CHAR, 164 },
129 { "M-%", KEY_IS_CHAR, 165 },
130 { "M-&", KEY_IS_CHAR, 166 },
131 { "M-'", KEY_IS_CHAR, 167 },
132 { "M-(", KEY_IS_CHAR, 168 },
133 { "M-)", KEY_IS_CHAR, 169 },
134 { "M-*", KEY_IS_CHAR, 170 },
135 { "M-+", KEY_IS_CHAR, 171 },
136 { "M-,", KEY_IS_CHAR, 172 },
137 { "M--", KEY_IS_CHAR, 173 },
138 { "M-.", KEY_IS_CHAR, 174 },
139 { "M-/", KEY_IS_CHAR, 175 },
140 { "M-0", KEY_IS_CHAR, 176 },
141 { "M-1", KEY_IS_CHAR, 177 },
142 { "M-2", KEY_IS_CHAR, 178 },
143 { "M-3", KEY_IS_CHAR, 179 },
144 { "M-4", KEY_IS_CHAR, 180 },
145 { "M-5", KEY_IS_CHAR, 181 },
146 { "M-6", KEY_IS_CHAR, 182 },
147 { "M-7", KEY_IS_CHAR, 183 },
148 { "M-8", KEY_IS_CHAR, 184 },
149 { "M-9", KEY_IS_CHAR, 185 },
150 { "M-:", KEY_IS_CHAR, 186 },
151 { "M-;", KEY_IS_CHAR, 187 },
152 { "M-<", KEY_IS_CHAR, 188 },
153 { "M-=", KEY_IS_CHAR, 189 },
154 { "M->", KEY_IS_CHAR, 190 },
155 { "M-?", KEY_IS_CHAR, 191 },
156 { "M-@", KEY_IS_CHAR, 192 },
157 { "M-A", KEY_IS_CHAR, 193 },
158 { "M-B", KEY_IS_CHAR, 194 },
159 { "M-C", KEY_IS_CHAR, 195 },
160 { "M-D", KEY_IS_CHAR, 196 },
161 { "M-E", KEY_IS_CHAR, 197 },
162 { "M-F", KEY_IS_CHAR, 198 },
163 { "M-G", KEY_IS_CHAR, 199 },
164 { "M-H", KEY_IS_CHAR, 200 },
165 { "M-I", KEY_IS_CHAR, 201 },
166 { "M-J", KEY_IS_CHAR, 202 },
167 { "M-K", KEY_IS_CHAR, 203 },
168 { "M-L", KEY_IS_CHAR, 204 },
169 { "M-M", KEY_IS_CHAR, 205 },
170 { "M-N", KEY_IS_CHAR, 206 },
171 { "M-O", KEY_IS_CHAR, 207 },
172 { "M-P", KEY_IS_CHAR, 208 },
173 { "M-Q", KEY_IS_CHAR, 209 },
174 { "M-R", KEY_IS_CHAR, 210 },
175 { "M-S", KEY_IS_CHAR, 211 },
176 { "M-T", KEY_IS_CHAR, 212 },
177 { "M-U", KEY_IS_CHAR, 213 },
178 { "M-V", KEY_IS_CHAR, 214 },
179 { "M-W", KEY_IS_CHAR, 215 },
180 { "M-X", KEY_IS_CHAR, 216 },
181 { "M-Y", KEY_IS_CHAR, 217 },
182 { "M-Z", KEY_IS_CHAR, 218 },
183 { "M-[", KEY_IS_CHAR, 219 },
184 { "M-\\", KEY_IS_CHAR, 220 },
185 { "M-]", KEY_IS_CHAR, 221 },
186 { "M-^", KEY_IS_CHAR, 222 },
187 { "M-^?", KEY_IS_CHAR, 255 },
188 { "M-^@", KEY_IS_CHAR, 128 },
189 { "M-^A", KEY_IS_CHAR, 129 },
190 { "M-^B", KEY_IS_CHAR, 130 },
191 { "M-^C", KEY_IS_CHAR, 131 },
192 { "M-^D", KEY_IS_CHAR, 132 },
193 { "M-^E", KEY_IS_CHAR, 133 },
194 { "M-^F", KEY_IS_CHAR, 134 },
195 { "M-^G", KEY_IS_CHAR, 135 },
196 { "M-^H", KEY_IS_CHAR, 136 },
197 { "M-^I", KEY_IS_CHAR, 137 },
198 { "M-^J", KEY_IS_CHAR, 138 },
199 { "M-^K", KEY_IS_CHAR, 139 },
200 { "M-^L", KEY_IS_CHAR, 140 },
201 { "M-^M", KEY_IS_CHAR, 141 },
202 { "M-^N", KEY_IS_CHAR, 142 },
203 { "M-^O", KEY_IS_CHAR, 143 },
204 { "M-^P", KEY_IS_CHAR, 144 },
205 { "M-^Q", KEY_IS_CHAR, 145 },
206 { "M-^R", KEY_IS_CHAR, 146 },
207 { "M-^S", KEY_IS_CHAR, 147 },
208 { "M-^T", KEY_IS_CHAR, 148 },
209 { "M-^U", KEY_IS_CHAR, 149 },
210 { "M-^V", KEY_IS_CHAR, 150 },
211 { "M-^W", KEY_IS_CHAR, 151 },
212 { "M-^X", KEY_IS_CHAR, 152 },
213 { "M-^Y", KEY_IS_CHAR, 153 },
214 { "M-^Z", KEY_IS_CHAR, 154 },
215 { "M-^[", KEY_IS_CHAR, 155 },
216 { "M-^\\", KEY_IS_CHAR, 156 },
217 { "M-^]", KEY_IS_CHAR, 157 },
218 { "M-^^", KEY_IS_CHAR, 158 },
219 { "M-^_", KEY_IS_CHAR, 159 },
220 { "M-_", KEY_IS_CHAR, 223 },
221 { "M-`", KEY_IS_CHAR, 224 },
222 { "M-a", KEY_IS_CHAR, 225 },
223 { "M-b", KEY_IS_CHAR, 226 },
224 { "M-c", KEY_IS_CHAR, 227 },
225 { "M-d", KEY_IS_CHAR, 228 },
226 { "M-e", KEY_IS_CHAR, 229 },
227 { "M-f", KEY_IS_CHAR, 230 },
228 { "M-g", KEY_IS_CHAR, 231 },
229 { "M-h", KEY_IS_CHAR, 232 },
230 { "M-i", KEY_IS_CHAR, 233 },
231 { "M-j", KEY_IS_CHAR, 234 },
232 { "M-k", KEY_IS_CHAR, 235 },
233 { "M-l", KEY_IS_CHAR, 236 },
234 { "M-m", KEY_IS_CHAR, 237 },
235 { "M-n", KEY_IS_CHAR, 238 },
236 { "M-o", KEY_IS_CHAR, 239 },
237 { "M-p", KEY_IS_CHAR, 240 },
238 { "M-q", KEY_IS_CHAR, 241 },
239 { "M-r", KEY_IS_CHAR, 242 },
240 { "M-s", KEY_IS_CHAR, 243 },
241 { "M-space", KEY_IS_CHAR, 160 },
242 { "M-t", KEY_IS_CHAR, 244 },
243 { "M-u", KEY_IS_CHAR, 245 },
244 { "M-v", KEY_IS_CHAR, 246 },
245 { "M-w", KEY_IS_CHAR, 247 },
246 { "M-x", KEY_IS_CHAR, 248 },
247 { "M-y", KEY_IS_CHAR, 249 },
248 { "M-z", KEY_IS_CHAR, 250 },
249 { "M-{", KEY_IS_CHAR, 251 },
250 { "M-|", KEY_IS_CHAR, 252 },
251 { "M-}", KEY_IS_CHAR, 253 },
252 { "M-~", KEY_IS_CHAR, 254 },
253 { "N", KEY_IS_CHAR, 78 },
254 { "O", KEY_IS_CHAR, 79 },
255 { "P", KEY_IS_CHAR, 80 },
256 { "Q", KEY_IS_CHAR, 81 },
257 { "R", KEY_IS_CHAR, 82 },
258 { "S", KEY_IS_CHAR, 83 },
259 { "S-begin", KEY_SBEG, 0 },
260 { "S-cancel", KEY_SCANCEL, 0 },
261 { "S-command", KEY_SCOMMAND, 0 },
262 { "S-copy", KEY_SCOPY, 0 },
263 { "S-create", KEY_SCREATE, 0 },
264 { "S-del_line", KEY_SDL, 0 },
265 { "S-delete", KEY_SDC, 0 },
266 { "S-eol", KEY_SEOL, 0 },
267 { "S-exit", KEY_SEXIT, 0 },
268 { "S-find", KEY_SFIND, 0 },
269 { "S-help", KEY_SHELP, 0 },
270 { "S-home", KEY_SHOME, 0 },
271 { "S-insert", KEY_SIC, 0 },
272 { "S-left", KEY_SLEFT, 0 },
273 { "S-message", KEY_SMESSAGE, 0 },
274 { "S-move", KEY_SMOVE, 0 },
275 { "S-next", KEY_SNEXT, 0 },
276 { "S-options", KEY_SOPTIONS, 0 },
277 { "S-previous", KEY_SPREVIOUS, 0 },
278 { "S-print", KEY_SPRINT, 0 },
279 { "S-redo", KEY_SREDO, 0 },
280 { "S-replace", KEY_SREPLACE, 0 },
281 { "S-resume", KEY_SRSUME, 0 },
282 { "S-right", KEY_SRIGHT, 0 },
283 { "S-save", KEY_SSAVE, 0 },
284 { "S-suspend", KEY_SSUSPEND, 0 },
285 { "S-undo", KEY_SUNDO, 0 },
286 { "T", KEY_IS_CHAR, 84 },
287 { "U", KEY_IS_CHAR, 85 },
288 { "V", KEY_IS_CHAR, 86 },
289 { "W", KEY_IS_CHAR, 87 },
290 { "X", KEY_IS_CHAR, 88 },
291 { "Y", KEY_IS_CHAR, 89 },
292 { "Z", KEY_IS_CHAR, 90 },
293 { "[", KEY_IS_CHAR, 91 },
294 { "\\", KEY_IS_CHAR, 92 },
295 { "]", KEY_IS_CHAR, 93 },
296 { "^", KEY_IS_CHAR, 94 },
297 { "^A", KEY_IS_CHAR, 1 },
298 { "^B", KEY_IS_CHAR, 2 },
299 { "^C", KEY_IS_CHAR, 3 },
300 { "^D", KEY_IS_CHAR, 4 },
301 { "^E", KEY_IS_CHAR, 5 },
302 { "^F", KEY_IS_CHAR, 6 },
303 { "^G", KEY_IS_CHAR, 7 },
304 { "^H", KEY_IS_CHAR, 8 },
305 { "^K", KEY_IS_CHAR, 11 },
306 { "^L", KEY_IS_CHAR, 12 },
307 { "^M", KEY_IS_CHAR, 13 },
308 { "^N", KEY_IS_CHAR, 14 },
309 { "^O", KEY_IS_CHAR, 15 },
310 { "^P", KEY_IS_CHAR, 16 },
311 { "^Q", KEY_IS_CHAR, 17 },
312 { "^R", KEY_IS_CHAR, 18 },
313 { "^S", KEY_IS_CHAR, 19 },
314 { "^T", KEY_IS_CHAR, 20 },
315 { "^U", KEY_IS_CHAR, 21 },
316 { "^V", KEY_IS_CHAR, 22 },
317 { "^W", KEY_IS_CHAR, 23 },
318 { "^X", KEY_IS_CHAR, 24 },
319 { "^Y", KEY_IS_CHAR, 25 },
320 { "^Z", KEY_IS_CHAR, 26 },
321 { "^\\", KEY_IS_CHAR, 28 },
322 { "^]", KEY_IS_CHAR, 29 },
323 { "^^", KEY_IS_CHAR, 30 },
324 { "^_", KEY_IS_CHAR, 31 },
325 { "_", KEY_IS_CHAR, 95 },
326 { "`", KEY_IS_CHAR, 96 },
327 { "a", KEY_IS_CHAR, 97 },
328 { "b", KEY_IS_CHAR, 98 },
329 { "back_tab", KEY_BTAB, 0 },
330 { "backspace", KEY_BACKSPACE, 127 }, /* NOTE: both key and ch */
331 { "begin", KEY_BEG, 0 },
332 { "c", KEY_IS_CHAR, 99 },
333 { "cancel", KEY_CANCEL, 0 },
334 { "clear", KEY_CLEAR, 0 },
335 { "clear_all_tabs", KEY_CATAB, 0 },
336 { "clear_tab", KEY_CTAB, 0 },
337 { "close", KEY_CLOSE, 0 },
338 { "command", KEY_COMMAND, 0 },
339 { "copy", KEY_COPY, 0 },
340 { "create", KEY_CREATE, 0 },
341 { "d", KEY_IS_CHAR, 100 },
342 { "del_line", KEY_DL, 0 },
343 { "delete", KEY_DC, 0 },
344 { "down", KEY_DOWN, 0 },
345 { "e", KEY_IS_CHAR, 101 },
346 { "eic", KEY_EIC, 0 },
347 { "end", KEY_END, 0 },
348 { "enter", KEY_IS_CHAR, 10 },
349 { "eol", KEY_EOL, 0 },
350 { "eos", KEY_EOS, 0 },
351 { "exit", KEY_EXIT, 0 },
352 { "f", KEY_IS_CHAR, 102 },
353 { "find", KEY_FIND, 0 },
354 { "g", KEY_IS_CHAR, 103 },
355 { "h", KEY_IS_CHAR, 104 },
356 { "help", KEY_HELP, 0 },
357 { "home", KEY_HOME, 0 },
358 { "i", KEY_IS_CHAR, 105 },
359 { "ins_line", KEY_IL, 0 },
360 { "insert", KEY_IC, 0 },
361 { "j", KEY_IS_CHAR, 106 },
362 { "k", KEY_IS_CHAR, 107 },
363 { "l", KEY_IS_CHAR, 108 },
364 { "left", KEY_LEFT, 0 },
365 { "lower_left", KEY_LL, 0 },
366 { "m", KEY_IS_CHAR, 109 },
367 { "mark", KEY_MARK, 0 },
368 { "message", KEY_MESSAGE, 0 },
369 { "move", KEY_MOVE, 0 },
370 { "n", KEY_IS_CHAR, 110 },
371 { "next", KEY_NEXT, 0 },
372 { "o", KEY_IS_CHAR, 111 },
373 { "open", KEY_OPEN, 0 },
374 { "options", KEY_OPTIONS, 0 },
375 { "p", KEY_IS_CHAR, 112 },
376 { "page_down", KEY_NPAGE, 0 },
377 { "page_up", KEY_PPAGE, 0 },
378 { "previous", KEY_PREVIOUS, 0 },
379 { "print", KEY_PRINT, 0 },
380 { "q", KEY_IS_CHAR, 113 },
381 { "r", KEY_IS_CHAR, 114 },
382 { "redo", KEY_REDO, 0 },
383 { "reference", KEY_REFERENCE, 0 },
384 { "refresh", KEY_REFRESH, 0 },
385 { "replace", KEY_REPLACE, 0 },
386 { "restart", KEY_RESTART, 0 },
387 { "resume", KEY_RESUME, 0 },
388 { "right", KEY_RIGHT, 0 },
389 { "s", KEY_IS_CHAR, 115 },
390 { "save", KEY_SAVE, 0 },
391 { "scroll_b", KEY_SR, 0 },
392 { "scroll_f", KEY_SF, 0 },
393 { "select", KEY_SELECT, 0 },
394 { "send", KEY_SEND, 0 },
395 { "set_tab", KEY_STAB, 0 },
396 { "space", KEY_IS_CHAR, 32 },
397 { "suspend", KEY_SUSPEND, 0 },
398 { "t", KEY_IS_CHAR, 116 },
399 { "tab", KEY_IS_CHAR, 9 },
400 { "u", KEY_IS_CHAR, 117 },
401 { "undo", KEY_UNDO, 0 },
402 { "up", KEY_UP, 0 },
403 { "v", KEY_IS_CHAR, 118 },
404 { "w", KEY_IS_CHAR, 119 },
405 { "x", KEY_IS_CHAR, 120 },
406 { "y", KEY_IS_CHAR, 121 },
407 { "z", KEY_IS_CHAR, 122 },
408 { "{", KEY_IS_CHAR, 123 },
409 { "|", KEY_IS_CHAR, 124 },
410 { "}", KEY_IS_CHAR, 125 },
411 { "~", KEY_IS_CHAR, 126 },
412 { NULL, 0, 0 }
414 /* }}} */
416 static int find_context(const char *name)
418 int i;
420 for (i = 0; i < NR_CTXS; i++) {
421 if (strcmp(name, key_context_names[i]) == 0)
422 return i;
424 error_msg("invalid context '%s'", name);
425 return -1;
428 static const struct key *find_key(const char *name)
430 int i;
432 for (i = 0; key_table[i].name; i++) {
433 if (strcmp(name, key_table[i].name) == 0)
434 return &key_table[i];
436 error_msg("invalid key '%s'", name);
437 return NULL;
440 static struct binding *find_binding(enum key_context c, const struct key *k)
442 struct binding *b = key_bindings[c];
444 while (b) {
445 if (b->key == k)
446 break;
447 b = b->next;
449 return b;
452 void show_binding(const char *context, const char *key)
454 const struct key *k;
455 const struct binding *b;
456 int c;
458 c = find_context(context);
459 if (c < 0)
460 return;
462 k = find_key(key);
463 if (k == NULL)
464 return;
466 b = find_binding(c, k);
467 if (b == NULL) {
468 info_msg("No such binding");
469 } else {
470 info_msg("bind %s %s %s", context, key, b->cmd);
474 int key_bind(const char *context, const char *key, const char *cmd, int force)
476 const struct key *k;
477 struct binding *b, *ptr, *prev;
478 struct command *command;
479 int c, size;
481 c = find_context(context);
482 if (c < 0)
483 return -1;
485 k = find_key(key);
486 if (k == NULL)
487 return -1;
489 /* check if already bound */
490 b = find_binding(c, k);
491 if (b) {
492 if (!force)
493 goto bound;
494 key_unbind(context, key, 0);
497 if (*cmd == ':')
498 cmd++;
499 size = strlen(cmd) + 1;
501 b = xmalloc(sizeof(struct binding) + size);
502 b->key = k;
503 b->ctx = c;
504 memcpy(b->cmd, cmd, size);
506 /* insert keeping sorted by key */
507 prev = NULL;
508 ptr = key_bindings[c];
509 while (ptr) {
510 if (strcmp(b->key->name, ptr->key->name) < 0)
511 break;
512 prev = ptr;
513 ptr = ptr->next;
515 b->next = ptr;
516 if (prev) {
517 prev->next = b;
518 } else {
519 key_bindings[c] = b;
521 command = get_command(cmd);
522 if (command && !command->bc++)
523 help_remove_unbound(command);
524 help_add_bound(b);
525 return 0;
526 bound:
527 error_msg("key %s already bound in context %s", key, key_context_names[c]);
528 return -1;
531 int key_unbind(const char *context, const char *key, int force)
533 enum key_context c;
534 const struct key *k;
535 struct binding *b, *prev;
536 struct command *command;
538 c = find_context(context);
539 if (c < 0)
540 return -1;
542 k = find_key(key);
543 if (k == NULL)
544 return -1;
546 prev = NULL;
547 b = key_bindings[c];
548 while (b) {
549 if (b->key == k) {
550 if (prev) {
551 prev->next = b->next;
552 } else {
553 key_bindings[c] = b->next;
555 command = get_command(b->cmd);
556 if (command && !--command->bc)
557 help_add_unbound(command);
558 help_remove_bound(b);
559 free(b);
560 return 0;
562 prev = b;
563 b = b->next;
565 if (!force) {
566 error_msg("key %s not bound in context %s", key, context);
567 return -1;
569 return 0;
572 static int handle_key(const struct binding *b, const struct key *k)
574 while (b) {
575 if (b->key == k) {
576 run_command(b->cmd);
577 return 1;
579 b = b->next;
581 return 0;
584 static const struct key *ch_to_key(uchar ch)
586 int i;
588 for (i = 0; key_table[i].name; i++) {
589 if (key_table[i].ch == ch)
590 return &key_table[i];
592 return NULL;
595 static const struct key *keycode_to_key(int key)
597 int i;
599 for (i = 0; key_table[i].name; i++) {
600 if (key_table[i].key != KEY_IS_CHAR && key_table[i].key == key)
601 return &key_table[i];
603 return NULL;
606 void normal_mode_ch(uchar ch)
608 enum key_context c;
609 const struct key *k;
611 /* you can't redefine these keys */
612 switch (ch) {
613 case ':':
614 enter_command_mode();
615 return;
616 case '/':
617 enter_search_mode();
618 return;
619 case '?':
620 enter_search_backward_mode();
621 return;
624 c = view_to_context[cur_view];
625 k = ch_to_key(ch);
627 if (k == NULL) {
628 return;
631 /* view-specific ch */
632 if (handle_key(key_bindings[c], k))
633 return;
635 /* common ch */
636 handle_key(key_bindings[CTX_COMMON], k);
639 void normal_mode_key(int key)
641 enum key_context c = view_to_context[cur_view];
642 const struct key *k = keycode_to_key(key);
644 if (k == NULL) {
645 return;
648 /* view-specific key */
649 if (handle_key(key_bindings[c], k))
650 return;
652 /* common key */
653 handle_key(key_bindings[CTX_COMMON], k);