source: keymap.c @ 700dc91

release-1.6release-1.7release-1.8release-1.9
Last change on this file since 700dc91 was 8a921b5, checked in by Leonid Grinberg <leonidg@mit.edu>, 12 years ago
Add new command unbindkey for removing keybindings
  • Property mode set to 100644
File size: 8.8 KB
RevLine 
[7d4fbcd]1#include <string.h>
2#include "owl.h"
3
4/* returns 0 on success */
[e19eb97]5int 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]6{
[7d4fbcd]7  if (!name || !desc) return(-1);
8  if ((km->name = owl_strdup(name)) == NULL) return(-1);
9  if ((km->desc = owl_strdup(desc)) == NULL) return(-1);
10  if (0 != owl_list_create(&km->bindings)) return(-1);
11  km->submap = NULL;
12  km->default_fn = default_fn;
13  km->prealways_fn = prealways_fn;
14  km->postalways_fn = postalways_fn;
15  return(0);
16}
17
18/* note that this will free the memory for the bindings! */
[0bdb3a6]19void owl_keymap_cleanup(owl_keymap *km)
[cf83b7a]20{
[7d4fbcd]21  owl_free(km->name);
22  owl_free(km->desc);
[920201c]23  owl_list_cleanup(&km->bindings, (void (*)(void *))owl_keybinding_delete);
[7d4fbcd]24}
25
[afa200a]26void owl_keymap_set_submap(owl_keymap *km, const owl_keymap *submap)
[cf83b7a]27{
[7d4fbcd]28  km->submap = submap;
29}
30
31/* creates and adds a key binding */
[e19eb97]32int owl_keymap_create_binding(owl_keymap *km, const char *keyseq, const char *command, void (*function_fn)(void), const char *desc)
[cf83b7a]33{
[7d4fbcd]34  owl_keybinding *kb, *curkb;
35  int i;
36
37  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
38  if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) {
39    owl_free(kb);
40    return(-1);
41  }
42  /* see if another matching binding, and if so remove it.
43   * otherwise just add this one.
44   */
45  for (i = owl_list_get_size(&km->bindings)-1; i>=0; i--) {
[4d86e06]46    curkb = owl_list_get_element(&km->bindings, i);
[7d4fbcd]47    if (owl_keybinding_equal(curkb, kb)) {
48      owl_list_remove_element(&km->bindings, i);
[920201c]49      owl_keybinding_delete(curkb);
[7d4fbcd]50    }
51  }
52  return owl_list_append_element(&km->bindings, kb); 
[8a921b5]53
[7d4fbcd]54}
55
[8a921b5]56/* removes the binding associated with the keymap */
57int owl_keymap_remove_binding(owl_keymap *km, const char *keyseq)
58{
59  owl_keybinding *kb, *curkb;
60  int i;
61
62  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
63  if (0 != owl_keybinding_make_keys(kb, keyseq)) {
64    owl_free(kb);
65    return(-1);
66  }
67
68  for (i = owl_list_get_size(&km->bindings)-1; i >= 0; i--) {
69    curkb = owl_list_get_element(&km->bindings, i);
70    if (owl_keybinding_equal(curkb, kb)) {
71      owl_list_remove_element(&km->bindings, i);
72      owl_keybinding_delete(curkb);
73      return(0);
74    }
75  }
76  return(-2);
77}
78
79
[7d4fbcd]80/* returns a summary line describing this keymap.  the caller must free. */
[afa200a]81char *owl_keymap_summary(const owl_keymap *km)
[cf83b7a]82{
[7d4fbcd]83  if (!km || !km->name || !km->desc) return NULL;
[36486be]84  return owl_sprintf("%-15s - %s", km->name, km->desc);
[7d4fbcd]85}
86
87/* Appends details about the keymap to fm */
[afa200a]88void owl_keymap_get_details(const owl_keymap *km, owl_fmtext *fm)
[cf83b7a]89{
[7d4fbcd]90  int i, nbindings; 
[f1d7d0f]91  const owl_keybinding *kb;
[7d4fbcd]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->submap) {
102    owl_fmtext_append_normal(fm, OWL_TABSTR "Has submap: ");
103    owl_fmtext_append_normal(fm, km->submap->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  nbindings = owl_list_get_size(&km->bindings);
122  for (i=0; i<nbindings; i++) {
123    char buff[100];
[0a0fb74]124    const owl_cmd *cmd;
[e19eb97]125    const char *tmpdesc, *desc = "";
[7d4fbcd]126
127    kb = owl_list_get_element(&km->bindings, i);
128    owl_keybinding_tostring(kb, buff, 100);
129    owl_fmtext_append_normal(fm, OWL_TABSTR);
130    owl_fmtext_append_normal(fm, buff);
131    owl_fmtext_append_spaces(fm, 11-strlen(buff));
132    owl_fmtext_append_normal(fm, " - ");
133    if (kb->desc && *kb->desc) {
134      desc = kb->desc;
135    } else if ((cmd=owl_function_get_cmd(kb->command))
136               && (tmpdesc = owl_cmd_get_summary(cmd))) {
137      desc = tmpdesc;
138    }
139    owl_fmtext_append_normal(fm, desc);
140    if (kb->command && *(kb->command)) {
141      owl_fmtext_append_normal(fm, "   [");
142      owl_fmtext_append_normal(fm, kb->command);
143      owl_fmtext_append_normal(fm, "]");
144    } 
145    owl_fmtext_append_normal(fm, "\n");
146  }
147}
148
149
150
151/***********************************************************************/
152/***************************** KEYHANDLER ******************************/
153/***********************************************************************/
154
155/* NOTE: keyhandler has private access to the internals of keymap */
156
[cf83b7a]157int owl_keyhandler_init(owl_keyhandler *kh)
158{
[7d4fbcd]159  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
160  kh->active = NULL;
161  owl_keyhandler_reset(kh);
162  return(0);
163}
164
165/* adds a new keymap */
[cf83b7a]166void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
167{
[7d4fbcd]168  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
169}
170
[e19eb97]171owl_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]172{
[7d4fbcd]173  owl_keymap *km;
[4d86e06]174  km = owl_malloc(sizeof(owl_keymap));
[7d4fbcd]175  if (!km) return NULL;
176  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
177  owl_keyhandler_add_keymap(kh, km);
178  return km;
179}
180
181/* resets state and clears out key stack */
[cf83b7a]182void owl_keyhandler_reset(owl_keyhandler *kh)
183{
[7d4fbcd]184  kh->in_esc = 0;
185  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
186  kh->kpstackpos = -1;
187}
188
[12bc46a]189owl_keymap *owl_keyhandler_get_keymap(const owl_keyhandler *kh, const char *mapname)
[cf83b7a]190{
[4d86e06]191  return owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]192}
193
[8d4b521]194/* free the list with owl_cmddict_namelist_cleanup */
[12bc46a]195void owl_keyhandler_get_keymap_names(const owl_keyhandler *kh, owl_list *l)
[cf83b7a]196{
[7d4fbcd]197  owl_dict_get_keys(&kh->keymaps, l);
198}
199
[bb0d439]200void owl_keyhandler_keymap_namelist_cleanup(owl_list *l)
[cf83b7a]201{
[8c59178]202  owl_list_cleanup(l, owl_free);
[7d4fbcd]203}
204
205
206
207/* sets the active keymap, which will also reset any key state.
208 * returns the new keymap, or NULL on failure. */
[afa200a]209const owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
[cf83b7a]210{
[afa200a]211  const owl_keymap *km;
[7d4fbcd]212  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
[4d86e06]213  km = owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]214  if (!km) return(NULL);
215  owl_keyhandler_reset(kh);
216  kh->active = km;
217  return(km);
218}
219
220/* processes a keypress.  returns 0 if the keypress was handled,
221 * 1 if not handled, -1 on error, and -2 if j==ERR. */
[428834d]222int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
[cf83b7a]223{
[afa200a]224  const owl_keymap     *km;
[f1d7d0f]225  const owl_keybinding *kb;
[7d4fbcd]226  int i, match;
227
228  if (!kh->active) {
229    owl_function_makemsg("No active keymap!!!");
230    return(-1);
231  }
[7131cf2]232
[7d4fbcd]233  /* deal with ESC prefixing */
[428834d]234  if (!kh->in_esc && j.ch == 27) {
[7d4fbcd]235    kh->in_esc = 1;
236    return(0);
237  }
238  if (kh->in_esc) {
[428834d]239    j.ch = OWL_META(j.ch);
[7d4fbcd]240    kh->in_esc = 0;
241  }
242 
[428834d]243  kh->kpstack[++(kh->kpstackpos)] = j.ch;
[7d4fbcd]244  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
245    owl_keyhandler_reset(kh);
246    owl_function_makemsg("Too many prefix keys pressed...");
247    return(-1);
248  }
249
250  /* deal with the always_fn for the map and submaps */
251  for (km=kh->active; km; km=km->submap) {
252    if (km->prealways_fn) {
253      km->prealways_fn(j);
254    }
255  }
256
257  /* search for a match.  goes through active keymap and then
258   * through submaps... TODO:  clean this up so we can pull
259   * keyhandler and keymap apart.  */
260  for (km=kh->active; km; km=km->submap) {
261    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
[4d86e06]262      kb = owl_list_get_element(&km->bindings, i);
[e1b136bf]263      match = owl_keybinding_match(kb, kh);
[7d4fbcd]264      if (match == 1) {         /* subset match */
[10b866d]265
266        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
[7d4fbcd]267        return(0);
268      } else if (match == 2) {  /* exact match */
[10b866d]269        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
[428834d]270        owl_keybinding_execute(kb, j.ch);
[7d4fbcd]271        owl_keyhandler_reset(kh);
272        if (km->postalways_fn) {
273          km->postalways_fn(j);
274        }
275        return(0);
276      }
277    }
278  }
279
280  /* see if a default action exists for the active keymap */
281  if (kh->active->default_fn && kh->kpstackpos<1) {
[10b866d]282    /*owl_function_debugmsg("processkey: executing default_fn");*/
283
[7d4fbcd]284    kh->active->default_fn(j);
285    owl_keyhandler_reset(kh);
286    if (kh->active->postalways_fn) {
287      kh->active->postalways_fn(j);
288    }
289    return(0);
290  }
291
292  owl_keyhandler_invalidkey(kh);
293  /* unable to handle */
294  return(1); 
295}
296
[cf83b7a]297void owl_keyhandler_invalidkey(owl_keyhandler *kh)
298{
[7d4fbcd]299    char kbbuff[500];
[e1b136bf]300    owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1, kbbuff, 500);
[7d4fbcd]301    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
302    owl_keyhandler_reset(kh);
303}
Note: See TracBrowser for help on using the repository browser.