source: keymap.c @ ce68f23

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