source: keymap.c @ 488ebf6

owl
Last change on this file since 488ebf6 was fa00c5c, checked in by James M. Kretchmar <kretch@mit.edu>, 15 years ago
Correct license.
  • Property mode set to 100644
File size: 9.4 KB
Line 
1/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar
2 *
3 * This file is part of Owl.
4 *
5 * Owl is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Owl is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Owl.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 * ---------------------------------------------------------------
19 *
20 * As of Owl version 2.1.12 there are patches contributed by
21 * developers of the branched BarnOwl project, Copyright (c)
22 * 2006-2009 The BarnOwl Developers. All rights reserved.
23 */
24
25#include <string.h>
26#include "owl.h"
27
28static const char fileIdent[] = "$Id$";
29
30/* returns 0 on success */
31int owl_keymap_init(owl_keymap *km, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int))
32{
33  if (!name || !desc) return(-1);
34  if ((km->name = owl_strdup(name)) == NULL) return(-1);
35  if ((km->desc = owl_strdup(desc)) == NULL) return(-1);
36  if (0 != owl_list_create(&km->bindings)) return(-1);
37  km->submap = NULL;
38  km->default_fn = default_fn;
39  km->prealways_fn = prealways_fn;
40  km->postalways_fn = postalways_fn;
41  return(0);
42}
43
44/* note that this will free the memory for the bindings! */
45void owl_keymap_free(owl_keymap *km)
46{
47  owl_free(km->name);
48  owl_free(km->desc);
49  owl_list_free_all(&km->bindings, (void(*)(void*))owl_keybinding_free_all);
50}
51
52void owl_keymap_set_submap(owl_keymap *km, owl_keymap *submap)
53{
54  km->submap = submap;
55}
56
57/* creates and adds a key binding */
58int owl_keymap_create_binding(owl_keymap *km, char *keyseq, char *command, void (*function_fn)(void), char *desc)
59{
60  owl_keybinding *kb, *curkb;
61  int i;
62
63  if ((kb = owl_malloc(sizeof(owl_keybinding))) == NULL) return(-1);
64  if (0 != owl_keybinding_init(kb, keyseq, command, function_fn, desc)) {
65    owl_free(kb);
66    return(-1);
67  }
68  /* see if another matching binding, and if so remove it.
69   * otherwise just add this one.
70   */
71  for (i = owl_list_get_size(&km->bindings)-1; i>=0; i--) {
72    curkb = (owl_keybinding*)owl_list_get_element(&km->bindings, i);
73    if (owl_keybinding_equal(curkb, kb)) {
74      owl_list_remove_element(&km->bindings, i);
75      owl_keybinding_free_all(curkb);
76    }
77  }
78  return owl_list_append_element(&km->bindings, kb); 
79}
80
81/* returns a summary line describing this keymap.  the caller must free. */
82char *owl_keymap_summary(owl_keymap *km)
83{
84  char *s;
85  int slen;
86  if (!km || !km->name || !km->desc) return NULL;
87  slen = strlen(km->name)+strlen(km->desc)+20;
88  s = owl_malloc(slen);
89  snprintf(s, slen-1, "%-15s - %s", km->name, km->desc);
90  return s;
91}
92
93/* Appends details about the keymap to fm */
94void owl_keymap_get_details(owl_keymap *km, owl_fmtext *fm)
95{
96  int i, nbindings; 
97  owl_keybinding *kb;
98 
99  owl_fmtext_append_bold(fm, "KEYMAP - ");
100  owl_fmtext_append_bold(fm, km->name);
101  owl_fmtext_append_normal(fm, "\n");
102  if (km->desc) {
103    owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose:    ");
104    owl_fmtext_append_normal(fm, km->desc);
105    owl_fmtext_append_normal(fm, "\n");
106  }
107  if (km->submap) {
108    owl_fmtext_append_normal(fm, OWL_TABSTR "Has submap: ");
109    owl_fmtext_append_normal(fm, km->submap->name);
110    owl_fmtext_append_normal(fm, "\n");
111  }
112    owl_fmtext_append_normal(fm, "\n");
113  if (km->default_fn) {
114    owl_fmtext_append_normal(fm, OWL_TABSTR
115     "Has a default keypress handler (default_fn).\n");
116  }
117  if (km->prealways_fn) {
118    owl_fmtext_append_normal(fm, OWL_TABSTR
119     "Executes a function (prealways_fn) on every keypress.\n");
120  }
121  if (km->postalways_fn) {
122    owl_fmtext_append_normal(fm, OWL_TABSTR
123     "Executes a function (postalways_fn) after handling every keypress.\n");
124  }
125
126  owl_fmtext_append_bold(fm, "\nKey bindings:\n\n"); 
127  nbindings = owl_list_get_size(&km->bindings);
128  for (i=0; i<nbindings; i++) {
129    char buff[100];
130    owl_cmd *cmd;
131    char *tmpdesc, *desc = "";
132
133    kb = owl_list_get_element(&km->bindings, i);
134    owl_keybinding_tostring(kb, buff, 100);
135    owl_fmtext_append_normal(fm, OWL_TABSTR);
136    owl_fmtext_append_normal(fm, buff);
137    owl_fmtext_append_spaces(fm, 11-strlen(buff));
138    owl_fmtext_append_normal(fm, " - ");
139    if (kb->desc && *kb->desc) {
140      desc = kb->desc;
141    } else if ((cmd=owl_function_get_cmd(kb->command))
142               && (tmpdesc = owl_cmd_get_summary(cmd))) {
143      desc = tmpdesc;
144    }
145    owl_fmtext_append_normal(fm, desc);
146    if (kb->command && *(kb->command)) {
147      owl_fmtext_append_normal(fm, "   [");
148      owl_fmtext_append_normal(fm, kb->command);
149      owl_fmtext_append_normal(fm, "]");
150    } 
151    owl_fmtext_append_normal(fm, "\n");
152  }
153}
154
155
156
157/***********************************************************************/
158/***************************** KEYHANDLER ******************************/
159/***********************************************************************/
160
161/* NOTE: keyhandler has private access to the internals of keymap */
162
163int owl_keyhandler_init(owl_keyhandler *kh)
164{
165  if (0 != owl_dict_create(&kh->keymaps)) return(-1); 
166  kh->active = NULL;
167  owl_keyhandler_reset(kh);
168  return(0);
169}
170
171/* adds a new keymap */
172void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
173{
174  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
175}
176
177owl_keymap *owl_keyhandler_create_and_add_keymap(owl_keyhandler *kh, char *name, char *desc, void (*default_fn)(int), void (*prealways_fn)(int), void (*postalways_fn)(int))
178{
179  owl_keymap *km;
180  km = (owl_keymap*)owl_malloc(sizeof(owl_keymap));
181  if (!km) return NULL;
182  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
183  owl_keyhandler_add_keymap(kh, km);
184  return km;
185}
186
187/* resets state and clears out key stack */
188void owl_keyhandler_reset(owl_keyhandler *kh)
189{
190  kh->in_esc = 0;
191  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
192  kh->kpstackpos = -1;
193}
194
195owl_keymap *owl_keyhandler_get_keymap(owl_keyhandler *kh, char *mapname)
196{
197  return (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname);
198}
199
200/* free the list with owl_cmddict_namelist_free */
201void owl_keyhandler_get_keymap_names(owl_keyhandler *kh, owl_list *l)
202{
203  owl_dict_get_keys(&kh->keymaps, l);
204}
205
206void owl_keyhandler_keymap_namelist_free(owl_list *l)
207{
208  owl_list_free_all(l, owl_free);
209}
210
211
212
213/* sets the active keymap, which will also reset any key state.
214 * returns the new keymap, or NULL on failure. */
215owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, char *mapname)
216{
217  owl_keymap *km;
218  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
219  km = (owl_keymap*)owl_dict_find_element(&kh->keymaps, mapname);
220  if (!km) return(NULL);
221  owl_keyhandler_reset(kh);
222  kh->active = km;
223  return(km);
224}
225
226/* processes a keypress.  returns 0 if the keypress was handled,
227 * 1 if not handled, -1 on error, and -2 if j==ERR. */
228int owl_keyhandler_process(owl_keyhandler *kh, int j)
229{
230  owl_keymap     *km;
231  owl_keybinding *kb;
232  int i, match;
233
234  if (!kh->active) {
235    owl_function_makemsg("No active keymap!!!");
236    return(-1);
237  }
238
239  /* temporarily disallow C-`/C-SPACE until we fix associated bugs */
240  if (j==ERR || j==0) {
241        return(-1);
242  }
243
244  /*
245    owl_function_debugmsg("processkey: got key %d, active keymap %s, stack at %d",
246    j, kh->active->name, kh->kpstackpos);
247  */
248
249  /* deal with ESC prefixing */
250  if (!kh->in_esc && j==27) {
251    kh->in_esc = 1;
252    return(0);
253  }
254  if (kh->in_esc) {
255    j = OWL_META(j);
256    kh->in_esc = 0;
257  }
258 
259  kh->kpstack[++(kh->kpstackpos)] = j;
260  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
261    owl_keyhandler_reset(kh);
262    owl_function_makemsg("Too many prefix keys pressed...");
263    return(-1);
264  }
265
266  /* deal with the always_fn for the map and submaps */
267  for (km=kh->active; km; km=km->submap) {
268    if (km->prealways_fn) {
269      km->prealways_fn(j);
270    }
271  }
272
273  /* search for a match.  goes through active keymap and then
274   * through submaps... TODO:  clean this up so we can pull
275   * keyhandler and keymap apart.  */
276  for (km=kh->active; km; km=km->submap) {
277    for (i=owl_list_get_size(&km->bindings)-1; i>=0; i--) {
278      kb = (owl_keybinding*)owl_list_get_element(&km->bindings, i);
279      match = owl_keybinding_match(kb, kh->kpstack);
280      if (match == 1) {         /* subset match */
281
282        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
283        return(0);
284      } else if (match == 2) {  /* exact match */
285        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
286        owl_keybinding_execute(kb, j);
287        owl_keyhandler_reset(kh);
288        if (km->postalways_fn) {
289          km->postalways_fn(j);
290        }
291        return(0);
292      }
293    }
294  }
295
296  /* see if a default action exists for the active keymap */
297  if (kh->active->default_fn && kh->kpstackpos<1) {
298    /*owl_function_debugmsg("processkey: executing default_fn");*/
299
300    kh->active->default_fn(j);
301    owl_keyhandler_reset(kh);
302    if (kh->active->postalways_fn) {
303      kh->active->postalways_fn(j);
304    }
305    return(0);
306  }
307
308  owl_keyhandler_invalidkey(kh);
309  /* unable to handle */
310  return(1); 
311}
312
313void owl_keyhandler_invalidkey(owl_keyhandler *kh)
314{
315    char kbbuff[500];
316    owl_keybinding_stack_tostring(kh->kpstack, kbbuff, 500);
317    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
318    owl_keyhandler_reset(kh);
319}
Note: See TracBrowser for help on using the repository browser.