source: keymap.c @ 5550eb0

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