source: keymap.c @ 1077891a

release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 1077891a was e19eb97, checked in by Anders Kaseorg <andersk@mit.edu>, 12 years ago
Add const qualifiers for char * and void *. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 8.2 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! */
[cf83b7a]19void owl_keymap_free(owl_keymap *km)
20{
[7d4fbcd]21  owl_free(km->name);
22  owl_free(km->desc);
23  owl_list_free_all(&km->bindings, (void(*)(void*))owl_keybinding_free_all);
24}
25
[cf83b7a]26void owl_keymap_set_submap(owl_keymap *km, owl_keymap *submap)
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);
49      owl_keybinding_free_all(curkb);
50    }
51  }
52  return owl_list_append_element(&km->bindings, kb); 
53}
54
55/* returns a summary line describing this keymap.  the caller must free. */
[cf83b7a]56char *owl_keymap_summary(owl_keymap *km)
57{
[7d4fbcd]58  char *s;
59  int slen;
60  if (!km || !km->name || !km->desc) return NULL;
61  slen = strlen(km->name)+strlen(km->desc)+20;
62  s = owl_malloc(slen);
63  snprintf(s, slen-1, "%-15s - %s", km->name, km->desc);
64  return s;
65}
66
67/* Appends details about the keymap to fm */
[cf83b7a]68void owl_keymap_get_details(owl_keymap *km, owl_fmtext *fm)
69{
[7d4fbcd]70  int i, nbindings; 
71  owl_keybinding *kb;
72 
73  owl_fmtext_append_bold(fm, "KEYMAP - ");
74  owl_fmtext_append_bold(fm, km->name);
75  owl_fmtext_append_normal(fm, "\n");
76  if (km->desc) {
77    owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose:    ");
78    owl_fmtext_append_normal(fm, km->desc);
79    owl_fmtext_append_normal(fm, "\n");
80  }
81  if (km->submap) {
82    owl_fmtext_append_normal(fm, OWL_TABSTR "Has submap: ");
83    owl_fmtext_append_normal(fm, km->submap->name);
84    owl_fmtext_append_normal(fm, "\n");
85  }
86    owl_fmtext_append_normal(fm, "\n");
87  if (km->default_fn) {
88    owl_fmtext_append_normal(fm, OWL_TABSTR
89     "Has a default keypress handler (default_fn).\n");
90  }
91  if (km->prealways_fn) {
92    owl_fmtext_append_normal(fm, OWL_TABSTR
93     "Executes a function (prealways_fn) on every keypress.\n");
94  }
95  if (km->postalways_fn) {
96    owl_fmtext_append_normal(fm, OWL_TABSTR
97     "Executes a function (postalways_fn) after handling every keypress.\n");
98  }
99
100  owl_fmtext_append_bold(fm, "\nKey bindings:\n\n"); 
101  nbindings = owl_list_get_size(&km->bindings);
102  for (i=0; i<nbindings; i++) {
103    char buff[100];
104    owl_cmd *cmd;
[e19eb97]105    const char *tmpdesc, *desc = "";
[7d4fbcd]106
107    kb = owl_list_get_element(&km->bindings, i);
108    owl_keybinding_tostring(kb, buff, 100);
109    owl_fmtext_append_normal(fm, OWL_TABSTR);
110    owl_fmtext_append_normal(fm, buff);
111    owl_fmtext_append_spaces(fm, 11-strlen(buff));
112    owl_fmtext_append_normal(fm, " - ");
113    if (kb->desc && *kb->desc) {
114      desc = kb->desc;
115    } else if ((cmd=owl_function_get_cmd(kb->command))
116               && (tmpdesc = owl_cmd_get_summary(cmd))) {
117      desc = tmpdesc;
118    }
119    owl_fmtext_append_normal(fm, desc);
120    if (kb->command && *(kb->command)) {
121      owl_fmtext_append_normal(fm, "   [");
122      owl_fmtext_append_normal(fm, kb->command);
123      owl_fmtext_append_normal(fm, "]");
124    } 
125    owl_fmtext_append_normal(fm, "\n");
126  }
127}
128
129
130
131/***********************************************************************/
132/***************************** KEYHANDLER ******************************/
133/***********************************************************************/
134
135/* NOTE: keyhandler has private access to the internals of keymap */
136
[cf83b7a]137int owl_keyhandler_init(owl_keyhandler *kh)
138{
[7d4fbcd]139  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
140  kh->active = NULL;
141  owl_keyhandler_reset(kh);
142  return(0);
143}
144
145/* adds a new keymap */
[cf83b7a]146void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
147{
[7d4fbcd]148  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
149}
150
[e19eb97]151owl_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]152{
[7d4fbcd]153  owl_keymap *km;
[4d86e06]154  km = owl_malloc(sizeof(owl_keymap));
[7d4fbcd]155  if (!km) return NULL;
156  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
157  owl_keyhandler_add_keymap(kh, km);
158  return km;
159}
160
161/* resets state and clears out key stack */
[cf83b7a]162void owl_keyhandler_reset(owl_keyhandler *kh)
163{
[7d4fbcd]164  kh->in_esc = 0;
165  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
166  kh->kpstackpos = -1;
167}
168
[e19eb97]169owl_keymap *owl_keyhandler_get_keymap(owl_keyhandler *kh, const char *mapname)
[cf83b7a]170{
[4d86e06]171  return owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]172}
173
174/* free the list with owl_cmddict_namelist_free */
[cf83b7a]175void owl_keyhandler_get_keymap_names(owl_keyhandler *kh, owl_list *l)
176{
[7d4fbcd]177  owl_dict_get_keys(&kh->keymaps, l);
178}
179
[cf83b7a]180void owl_keyhandler_keymap_namelist_free(owl_list *l)
181{
[7d4fbcd]182  owl_list_free_all(l, owl_free);
183}
184
185
186
187/* sets the active keymap, which will also reset any key state.
188 * returns the new keymap, or NULL on failure. */
[e19eb97]189owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
[cf83b7a]190{
[7d4fbcd]191  owl_keymap *km;
192  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
[4d86e06]193  km = owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]194  if (!km) return(NULL);
195  owl_keyhandler_reset(kh);
196  kh->active = km;
197  return(km);
198}
199
200/* processes a keypress.  returns 0 if the keypress was handled,
201 * 1 if not handled, -1 on error, and -2 if j==ERR. */
[428834d]202int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
[cf83b7a]203{
[7d4fbcd]204  owl_keymap     *km;
205  owl_keybinding *kb;
206  int i, match;
207
208  if (!kh->active) {
209    owl_function_makemsg("No active keymap!!!");
210    return(-1);
211  }
[7131cf2]212
[7d4fbcd]213  /* deal with ESC prefixing */
[428834d]214  if (!kh->in_esc && j.ch == 27) {
[7d4fbcd]215    kh->in_esc = 1;
216    return(0);
217  }
218  if (kh->in_esc) {
[428834d]219    j.ch = OWL_META(j.ch);
[7d4fbcd]220    kh->in_esc = 0;
221  }
222 
[428834d]223  kh->kpstack[++(kh->kpstackpos)] = j.ch;
[7d4fbcd]224  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
225    owl_keyhandler_reset(kh);
226    owl_function_makemsg("Too many prefix keys pressed...");
227    return(-1);
228  }
229
230  /* deal with the always_fn for the map and submaps */
231  for (km=kh->active; km; km=km->submap) {
232    if (km->prealways_fn) {
233      km->prealways_fn(j);
234    }
235  }
236
237  /* search for a match.  goes through active keymap and then
238   * through submaps... TODO:  clean this up so we can pull
239   * keyhandler and keymap apart.  */
240  for (km=kh->active; km; km=km->submap) {
241    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
[4d86e06]242      kb = owl_list_get_element(&km->bindings, i);
[e1b136bf]243      match = owl_keybinding_match(kb, kh);
[7d4fbcd]244      if (match == 1) {         /* subset match */
[10b866d]245
246        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
[7d4fbcd]247        return(0);
248      } else if (match == 2) {  /* exact match */
[10b866d]249        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
[428834d]250        owl_keybinding_execute(kb, j.ch);
[7d4fbcd]251        owl_keyhandler_reset(kh);
252        if (km->postalways_fn) {
253          km->postalways_fn(j);
254        }
255        return(0);
256      }
257    }
258  }
259
260  /* see if a default action exists for the active keymap */
261  if (kh->active->default_fn && kh->kpstackpos<1) {
[10b866d]262    /*owl_function_debugmsg("processkey: executing default_fn");*/
263
[7d4fbcd]264    kh->active->default_fn(j);
265    owl_keyhandler_reset(kh);
266    if (kh->active->postalways_fn) {
267      kh->active->postalways_fn(j);
268    }
269    return(0);
270  }
271
272  owl_keyhandler_invalidkey(kh);
273  /* unable to handle */
274  return(1); 
275}
276
[cf83b7a]277void owl_keyhandler_invalidkey(owl_keyhandler *kh)
278{
[7d4fbcd]279    char kbbuff[500];
[e1b136bf]280    owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1, kbbuff, 500);
[7d4fbcd]281    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
282    owl_keyhandler_reset(kh);
283}
Note: See TracBrowser for help on using the repository browser.