source: keymap.c @ d427f08

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