source: keymap.c @ 7a70e26

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 7a70e26 was 44cc9ab, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
While we're at it, rename submap to parent Should be more consistent with other programs. Signed-off-by: David Benjamin <davidben@mit.edu>
  • Property mode set to 100644
File size: 9.5 KB
Line 
1#include <string.h>
2#include "owl.h"
3
4static void _owl_keymap_format_bindings(const owl_keymap *km, owl_fmtext *fm);
5static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm);
6
7/* returns 0 on success */
8int owl_keymap_init(owl_keymap *km, const char *name, const char *desc, void (*default_fn)(owl_input), void (*prealways_fn)(owl_input), void (*postalways_fn)(owl_input))
9{
10  if (!name || !desc) return(-1);
11  if ((km->name = owl_strdup(name)) == NULL) return(-1);
12  if ((km->desc = owl_strdup(desc)) == NULL) return(-1);
13  if (0 != owl_list_create(&km->bindings)) return(-1);
14  km->parent = NULL;
15  km->default_fn = default_fn;
16  km->prealways_fn = prealways_fn;
17  km->postalways_fn = postalways_fn;
18  return(0);
19}
20
21/* note that this will free the memory for the bindings! */
22void owl_keymap_cleanup(owl_keymap *km)
23{
24  owl_free(km->name);
25  owl_free(km->desc);
26  owl_list_cleanup(&km->bindings, (void (*)(void *))owl_keybinding_delete);
27}
28
29void owl_keymap_set_parent(owl_keymap *km, const owl_keymap *parent)
30{
31  km->parent = parent;
32}
33
34/* creates and adds a key binding */
35int owl_keymap_create_binding(owl_keymap *km, const char *keyseq, const char *command, void (*function_fn)(void), const char *desc)
36{
37  owl_keybinding *kb, *curkb;
38  int i;
39
40  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
41  if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) {
42    owl_free(kb);
43    return(-1);
44  }
45  /* see if another matching binding, and if so remove it.
46   * otherwise just add this one.
47   */
48  for (i = owl_list_get_size(&km->bindings)-1; i>=0; i--) {
49    curkb = owl_list_get_element(&km->bindings, i);
50    if (owl_keybinding_equal(curkb, kb)) {
51      owl_list_remove_element(&km->bindings, i);
52      owl_keybinding_delete(curkb);
53    }
54  }
55  return owl_list_append_element(&km->bindings, kb); 
56
57}
58
59/* removes the binding associated with the keymap */
60int owl_keymap_remove_binding(owl_keymap *km, const char *keyseq)
61{
62  owl_keybinding *kb, *curkb;
63  int i;
64
65  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
66  if (0 != owl_keybinding_make_keys(kb, keyseq)) {
67    owl_free(kb);
68    return(-1);
69  }
70
71  for (i = owl_list_get_size(&km->bindings)-1; i >= 0; i--) {
72    curkb = owl_list_get_element(&km->bindings, i);
73    if (owl_keybinding_equal(curkb, kb)) {
74      owl_list_remove_element(&km->bindings, i);
75      owl_keybinding_delete(curkb);
76      return(0);
77    }
78  }
79  return(-2);
80}
81
82
83/* returns a summary line describing this keymap.  the caller must free. */
84char *owl_keymap_summary(const owl_keymap *km)
85{
86  if (!km || !km->name || !km->desc) return NULL;
87  return owl_sprintf("%-15s - %s", km->name, km->desc);
88}
89
90/* Appends details about the keymap to fm */
91void owl_keymap_get_details(const owl_keymap *km, owl_fmtext *fm, int recurse)
92{
93  owl_fmtext_append_bold(fm, "KEYMAP - ");
94  owl_fmtext_append_bold(fm, km->name);
95  owl_fmtext_append_normal(fm, "\n");
96  if (km->desc) {
97    owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose:    ");
98    owl_fmtext_append_normal(fm, km->desc);
99    owl_fmtext_append_normal(fm, "\n");
100  }
101  if (km->parent) {
102    owl_fmtext_append_normal(fm, OWL_TABSTR "Has parent: ");
103    owl_fmtext_append_normal(fm, km->parent->name);
104    owl_fmtext_append_normal(fm, "\n");
105  }
106    owl_fmtext_append_normal(fm, "\n");
107  if (km->default_fn) {
108    owl_fmtext_append_normal(fm, OWL_TABSTR
109     "Has a default keypress handler (default_fn).\n");
110  }
111  if (km->prealways_fn) {
112    owl_fmtext_append_normal(fm, OWL_TABSTR
113     "Executes a function (prealways_fn) on every keypress.\n");
114  }
115  if (km->postalways_fn) {
116    owl_fmtext_append_normal(fm, OWL_TABSTR
117     "Executes a function (postalways_fn) after handling every keypress.\n");
118  }
119
120  owl_fmtext_append_bold(fm, "\nKey bindings:\n\n"); 
121  if (recurse) {
122    _owl_keymap_format_with_parents(km, fm);
123  } else {
124    _owl_keymap_format_bindings(km, fm);
125  }
126}
127
128static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm)
129{
130  while (km) {
131    _owl_keymap_format_bindings(km, fm);
132    km = km->parent;
133    if (km) {
134      owl_fmtext_append_bold(fm, "\nInherited from ");
135      owl_fmtext_append_bold(fm, km->name);
136      owl_fmtext_append_bold(fm, ":\n\n");
137    }
138  }
139}
140
141static void _owl_keymap_format_bindings(const owl_keymap *km, owl_fmtext *fm)
142{
143  int i, nbindings;
144  const owl_keybinding *kb;
145 
146  nbindings = owl_list_get_size(&km->bindings);
147  for (i=0; i<nbindings; i++) {
148    char buff[100];
149    const owl_cmd *cmd;
150    const char *tmpdesc, *desc = "";
151
152    kb = owl_list_get_element(&km->bindings, i);
153    owl_keybinding_tostring(kb, buff, 100);
154    owl_fmtext_append_normal(fm, OWL_TABSTR);
155    owl_fmtext_append_normal(fm, buff);
156    owl_fmtext_append_spaces(fm, 11-strlen(buff));
157    owl_fmtext_append_normal(fm, " - ");
158    if (kb->desc && *kb->desc) {
159      desc = kb->desc;
160    } else if ((cmd=owl_function_get_cmd(kb->command))
161               && (tmpdesc = owl_cmd_get_summary(cmd))) {
162      desc = tmpdesc;
163    }
164    owl_fmtext_append_normal(fm, desc);
165    if (kb->command && *(kb->command)) {
166      owl_fmtext_append_normal(fm, "   [");
167      owl_fmtext_append_normal(fm, kb->command);
168      owl_fmtext_append_normal(fm, "]");
169    } 
170    owl_fmtext_append_normal(fm, "\n");
171  }
172}
173
174
175
176/***********************************************************************/
177/***************************** KEYHANDLER ******************************/
178/***********************************************************************/
179
180/* NOTE: keyhandler has private access to the internals of keymap */
181
182int owl_keyhandler_init(owl_keyhandler *kh)
183{
184  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
185  kh->active = NULL;
186  owl_keyhandler_reset(kh);
187  return(0);
188}
189
190/* adds a new keymap */
191void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
192{
193  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
194}
195
196owl_keymap *owl_keyhandler_create_and_add_keymap(owl_keyhandler *kh, const char *name, const char *desc, void (*default_fn)(owl_input), void (*prealways_fn)(owl_input), void (*postalways_fn)(owl_input))
197{
198  owl_keymap *km;
199  km = owl_malloc(sizeof(owl_keymap));
200  if (!km) return NULL;
201  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
202  owl_keyhandler_add_keymap(kh, km);
203  return km;
204}
205
206/* resets state and clears out key stack */
207void owl_keyhandler_reset(owl_keyhandler *kh)
208{
209  kh->in_esc = 0;
210  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
211  kh->kpstackpos = -1;
212}
213
214owl_keymap *owl_keyhandler_get_keymap(const owl_keyhandler *kh, const char *mapname)
215{
216  return owl_dict_find_element(&kh->keymaps, mapname);
217}
218
219/* free the list with owl_cmddict_namelist_cleanup */
220void owl_keyhandler_get_keymap_names(const owl_keyhandler *kh, owl_list *l)
221{
222  owl_dict_get_keys(&kh->keymaps, l);
223}
224
225void owl_keyhandler_keymap_namelist_cleanup(owl_list *l)
226{
227  owl_list_cleanup(l, owl_free);
228}
229
230
231
232/* sets the active keymap, which will also reset any key state.
233 * returns the new keymap, or NULL on failure. */
234const owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
235{
236  const owl_keymap *km;
237  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
238  km = owl_dict_find_element(&kh->keymaps, mapname);
239  if (!km) return(NULL);
240  owl_keyhandler_reset(kh);
241  kh->active = km;
242  return(km);
243}
244
245/* processes a keypress.  returns 0 if the keypress was handled,
246 * 1 if not handled, -1 on error, and -2 if j==ERR. */
247int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
248{
249  const owl_keymap     *km;
250  const owl_keybinding *kb;
251  int i, match;
252
253  if (!kh->active) {
254    owl_function_makemsg("No active keymap!!!");
255    return(-1);
256  }
257
258  /* deal with ESC prefixing */
259  if (!kh->in_esc && j.ch == 27) {
260    kh->in_esc = 1;
261    return(0);
262  }
263  if (kh->in_esc) {
264    j.ch = OWL_META(j.ch);
265    kh->in_esc = 0;
266  }
267 
268  kh->kpstack[++(kh->kpstackpos)] = j.ch;
269  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
270    owl_keyhandler_reset(kh);
271    owl_function_makemsg("Too many prefix keys pressed...");
272    return(-1);
273  }
274
275  /* deal with the always_fn for the map and parents */
276  for (km=kh->active; km; km=km->parent) {
277    if (km->prealways_fn) {
278      km->prealways_fn(j);
279    }
280  }
281
282  /* search for a match.  goes through active keymap and then
283   * through parents... TODO:  clean this up so we can pull
284   * keyhandler and keymap apart.  */
285  for (km=kh->active; km; km=km->parent) {
286    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
287      kb = owl_list_get_element(&km->bindings, i);
288      match = owl_keybinding_match(kb, kh);
289      if (match == 1) {         /* subset match */
290
291        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
292        return(0);
293      } else if (match == 2) {  /* exact match */
294        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
295        owl_keybinding_execute(kb, j.ch);
296        owl_keyhandler_reset(kh);
297        if (km->postalways_fn) {
298          km->postalways_fn(j);
299        }
300        return(0);
301      }
302    }
303  }
304
305  /* see if a default action exists for the active keymap */
306  if (kh->active->default_fn && kh->kpstackpos<1) {
307    /*owl_function_debugmsg("processkey: executing default_fn");*/
308
309    kh->active->default_fn(j);
310    owl_keyhandler_reset(kh);
311    if (kh->active->postalways_fn) {
312      kh->active->postalways_fn(j);
313    }
314    return(0);
315  }
316
317  owl_keyhandler_invalidkey(kh);
318  /* unable to handle */
319  return(1); 
320}
321
322void owl_keyhandler_invalidkey(owl_keyhandler *kh)
323{
324    char kbbuff[500];
325    owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1, kbbuff, 500);
326    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
327    owl_keyhandler_reset(kh);
328}
Note: See TracBrowser for help on using the repository browser.