source: keymap.c @ f25df21

release-1.10release-1.8release-1.9
Last change on this file since f25df21 was f25df21, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Don't call owl_list_create in owl_dict_get_keys Until we get rid of this owl_list thing altogether, there should be a convention as to who initializes the thing. Otherwise, we leak memory from people initializing it too many times. Whoever reviews this probably wants to look over this very carefully in case I missed one of the owl_list_creates. Also kill the various wrappers over owl_list_cleanup as they are not the inverse of any operation.
  • Property mode set to 100644
File size: 9.3 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 = g_strdup(name)) == NULL) return(-1);
12  if ((km->desc = g_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  g_free(km->name);
25  g_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 = g_new(owl_keybinding, 1)) == NULL) return(-1);
41  if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) {
42    g_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 = g_new(owl_keybinding, 1)) == NULL) return(-1);
66  if (0 != owl_keybinding_make_keys(kb, keyseq)) {
67    g_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 g_strdup_printf("%-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 = g_new(owl_keymap, 1);
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
219void owl_keyhandler_get_keymap_names(const owl_keyhandler *kh, owl_list *l)
220{
221  owl_dict_get_keys(&kh->keymaps, l);
222}
223
224
225/* sets the active keymap, which will also reset any key state.
226 * returns the new keymap, or NULL on failure. */
227const owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
228{
229  const owl_keymap *km;
230  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
231  km = owl_dict_find_element(&kh->keymaps, mapname);
232  if (!km) return(NULL);
233  owl_keyhandler_reset(kh);
234  kh->active = km;
235  return(km);
236}
237
238/* processes a keypress.  returns 0 if the keypress was handled,
239 * 1 if not handled, -1 on error, and -2 if j==ERR. */
240int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
241{
242  const owl_keymap     *km;
243  const owl_keybinding *kb;
244  int i, match;
245
246  if (!kh->active) {
247    owl_function_makemsg("No active keymap!!!");
248    return(-1);
249  }
250
251  /* deal with ESC prefixing */
252  if (!kh->in_esc && j.ch == 27) {
253    kh->in_esc = 1;
254    return(0);
255  }
256  if (kh->in_esc) {
257    j.ch = OWL_META(j.ch);
258    kh->in_esc = 0;
259  }
260 
261  kh->kpstack[++(kh->kpstackpos)] = j.ch;
262  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
263    owl_keyhandler_reset(kh);
264    owl_function_makemsg("Too many prefix keys pressed...");
265    return(-1);
266  }
267
268  /* deal with the always_fn for the map and parents */
269  for (km=kh->active; km; km=km->parent) {
270    if (km->prealways_fn) {
271      km->prealways_fn(j);
272    }
273  }
274
275  /* search for a match.  goes through active keymap and then
276   * through parents... TODO:  clean this up so we can pull
277   * keyhandler and keymap apart.  */
278  for (km=kh->active; km; km=km->parent) {
279    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
280      kb = owl_list_get_element(&km->bindings, i);
281      match = owl_keybinding_match(kb, kh);
282      if (match == 1) {         /* subset match */
283
284        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
285        return(0);
286      } else if (match == 2) {  /* exact match */
287        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
288        owl_keybinding_execute(kb, j.ch);
289        owl_keyhandler_reset(kh);
290        if (km->postalways_fn) {
291          km->postalways_fn(j);
292        }
293        return(0);
294      }
295    }
296  }
297
298  /* see if a default action exists for the active keymap */
299  if (kh->active->default_fn && kh->kpstackpos<1) {
300    /*owl_function_debugmsg("processkey: executing default_fn");*/
301
302    kh->active->default_fn(j);
303    owl_keyhandler_reset(kh);
304    if (kh->active->postalways_fn) {
305      kh->active->postalways_fn(j);
306    }
307    return(0);
308  }
309
310  owl_keyhandler_invalidkey(kh);
311  /* unable to handle */
312  return(1); 
313}
314
315void owl_keyhandler_invalidkey(owl_keyhandler *kh)
316{
317    char kbbuff[500];
318    owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1, kbbuff, 500);
319    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
320    owl_keyhandler_reset(kh);
321}
Note: See TracBrowser for help on using the repository browser.