source: keymap.c @ 84a071f

release-1.10release-1.8release-1.9
Last change on this file since 84a071f was 4c7c21f, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
owl_dict_create also never fails And like everywhere else, we weren't checking the return values most of the time anyway.
  • Property mode set to 100644
File size: 9.1 KB
RevLine 
[7d4fbcd]1#include <string.h>
2#include "owl.h"
3
[13ebf92]4static void _owl_keymap_format_bindings(const owl_keymap *km, owl_fmtext *fm);
[44cc9ab]5static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm);
[13ebf92]6
[7d4fbcd]7/* returns 0 on success */
[e19eb97]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))
[cf83b7a]9{
[7d4fbcd]10  if (!name || !desc) return(-1);
[fda61d3]11  km->name = g_strdup(name);
12  km->desc = g_strdup(desc);
13  owl_list_create(&km->bindings);
[44cc9ab]14  km->parent = NULL;
[7d4fbcd]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! */
[0bdb3a6]22void owl_keymap_cleanup(owl_keymap *km)
[cf83b7a]23{
[ddbbcffa]24  g_free(km->name);
25  g_free(km->desc);
[920201c]26  owl_list_cleanup(&km->bindings, (void (*)(void *))owl_keybinding_delete);
[7d4fbcd]27}
28
[44cc9ab]29void owl_keymap_set_parent(owl_keymap *km, const owl_keymap *parent)
[cf83b7a]30{
[44cc9ab]31  km->parent = parent;
[7d4fbcd]32}
33
34/* creates and adds a key binding */
[e19eb97]35int owl_keymap_create_binding(owl_keymap *km, const char *keyseq, const char *command, void (*function_fn)(void), const char *desc)
[cf83b7a]36{
[7d4fbcd]37  owl_keybinding *kb, *curkb;
38  int i;
39
[b13daa0]40  kb = owl_keybinding_new(keyseq, command, function_fn, desc);
41  if (kb == NULL)
42    return -1;
[7d4fbcd]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--) {
[4d86e06]47    curkb = owl_list_get_element(&km->bindings, i);
[7d4fbcd]48    if (owl_keybinding_equal(curkb, kb)) {
49      owl_list_remove_element(&km->bindings, i);
[920201c]50      owl_keybinding_delete(curkb);
[7d4fbcd]51    }
52  }
53  return owl_list_append_element(&km->bindings, kb); 
[8a921b5]54
[7d4fbcd]55}
56
[8a921b5]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
[b13daa0]63  kb = owl_keybinding_new(keyseq, NULL, NULL, NULL);
64  if (kb == NULL)
65    return -1;
[8a921b5]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);
[b13daa0]72      owl_keybinding_delete(kb);
[8a921b5]73      return(0);
74    }
75  }
[b13daa0]76  owl_keybinding_delete(kb);
[8a921b5]77  return(-2);
78}
79
80
[7d4fbcd]81/* returns a summary line describing this keymap.  the caller must free. */
[afa200a]82char *owl_keymap_summary(const owl_keymap *km)
[cf83b7a]83{
[7d4fbcd]84  if (!km || !km->name || !km->desc) return NULL;
[3472845]85  return g_strdup_printf("%-15s - %s", km->name, km->desc);
[7d4fbcd]86}
87
88/* Appends details about the keymap to fm */
[13ebf92]89void owl_keymap_get_details(const owl_keymap *km, owl_fmtext *fm, int recurse)
[cf83b7a]90{
[7d4fbcd]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  }
[44cc9ab]99  if (km->parent) {
100    owl_fmtext_append_normal(fm, OWL_TABSTR "Has parent: ");
101    owl_fmtext_append_normal(fm, km->parent->name);
[7d4fbcd]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"); 
[13ebf92]119  if (recurse) {
[44cc9ab]120    _owl_keymap_format_with_parents(km, fm);
[13ebf92]121  } else {
122    _owl_keymap_format_bindings(km, fm);
123  }
124}
125
[44cc9ab]126static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm)
[13ebf92]127{
128  while (km) {
129    _owl_keymap_format_bindings(km, fm);
[44cc9ab]130    km = km->parent;
[13ebf92]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 
[7d4fbcd]144  nbindings = owl_list_get_size(&km->bindings);
145  for (i=0; i<nbindings; i++) {
[45e2c95]146    char *kbstr;
[0a0fb74]147    const owl_cmd *cmd;
[e19eb97]148    const char *tmpdesc, *desc = "";
[7d4fbcd]149
150    kb = owl_list_get_element(&km->bindings, i);
[45e2c95]151    kbstr = owl_keybinding_tostring(kb);
[7d4fbcd]152    owl_fmtext_append_normal(fm, OWL_TABSTR);
[45e2c95]153    owl_fmtext_append_normal(fm, kbstr);
154    owl_fmtext_append_spaces(fm, 11-strlen(kbstr));
155    g_free(kbstr);
[7d4fbcd]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
[4c7c21f]181void owl_keyhandler_init(owl_keyhandler *kh)
[cf83b7a]182{
[4c7c21f]183  owl_dict_create(&kh->keymaps);
[7d4fbcd]184  kh->active = NULL;
185  owl_keyhandler_reset(kh);
186}
187
188/* adds a new keymap */
[cf83b7a]189void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
190{
[7d4fbcd]191  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
192}
193
[e19eb97]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))
[cf83b7a]195{
[7d4fbcd]196  owl_keymap *km;
[96828e4]197  km = g_new(owl_keymap, 1);
[7d4fbcd]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 */
[cf83b7a]204void owl_keyhandler_reset(owl_keyhandler *kh)
205{
[7d4fbcd]206  kh->in_esc = 0;
207  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
208  kh->kpstackpos = -1;
209}
210
[12bc46a]211owl_keymap *owl_keyhandler_get_keymap(const owl_keyhandler *kh, const char *mapname)
[cf83b7a]212{
[4d86e06]213  return owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]214}
215
[12bc46a]216void owl_keyhandler_get_keymap_names(const owl_keyhandler *kh, owl_list *l)
[cf83b7a]217{
[7d4fbcd]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. */
[afa200a]224const owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
[cf83b7a]225{
[afa200a]226  const owl_keymap *km;
[7d4fbcd]227  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
[4d86e06]228  km = owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]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. */
[428834d]237int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
[cf83b7a]238{
[afa200a]239  const owl_keymap     *km;
[f1d7d0f]240  const owl_keybinding *kb;
[7d4fbcd]241  int i, match;
242
243  if (!kh->active) {
244    owl_function_makemsg("No active keymap!!!");
245    return(-1);
246  }
[7131cf2]247
[7d4fbcd]248  /* deal with ESC prefixing */
[428834d]249  if (!kh->in_esc && j.ch == 27) {
[7d4fbcd]250    kh->in_esc = 1;
251    return(0);
252  }
253  if (kh->in_esc) {
[428834d]254    j.ch = OWL_META(j.ch);
[7d4fbcd]255    kh->in_esc = 0;
256  }
257 
[428834d]258  kh->kpstack[++(kh->kpstackpos)] = j.ch;
[7d4fbcd]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
[44cc9ab]265  /* deal with the always_fn for the map and parents */
266  for (km=kh->active; km; km=km->parent) {
[7d4fbcd]267    if (km->prealways_fn) {
268      km->prealways_fn(j);
269    }
270  }
271
272  /* search for a match.  goes through active keymap and then
[44cc9ab]273   * through parents... TODO:  clean this up so we can pull
[7d4fbcd]274   * keyhandler and keymap apart.  */
[44cc9ab]275  for (km=kh->active; km; km=km->parent) {
[7d4fbcd]276    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
[4d86e06]277      kb = owl_list_get_element(&km->bindings, i);
[e1b136bf]278      match = owl_keybinding_match(kb, kh);
[7d4fbcd]279      if (match == 1) {         /* subset match */
[10b866d]280
281        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
[7d4fbcd]282        return(0);
283      } else if (match == 2) {  /* exact match */
[10b866d]284        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
[428834d]285        owl_keybinding_execute(kb, j.ch);
[7d4fbcd]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) {
[10b866d]297    /*owl_function_debugmsg("processkey: executing default_fn");*/
298
[7d4fbcd]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
[cf83b7a]312void owl_keyhandler_invalidkey(owl_keyhandler *kh)
313{
[45e2c95]314    char *kbbuff = owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1);
[7d4fbcd]315    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
[45e2c95]316    g_free(kbbuff);
[7d4fbcd]317    owl_keyhandler_reset(kh);
318}
Note: See TracBrowser for help on using the repository browser.