source: keymap.c @ e4524da

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