source: keymap.c @ 53d6269

release-1.10
Last change on this file since 53d6269 was 7dcef03, checked in by Anders Kaseorg <andersk@mit.edu>, 11 years ago
Use the Glib slice allocator for fixed-size objects The slice allocator, available since GLib 2.10, is more space-efficient than [g_]malloc. Since BarnOwl is obviously at the leading edge of space-efficient technology, this seems like a natural fit. Use it for every fixed-size object except owl_viewwin_search_data (which would need an extra destroy_cbdata function to g_slice_free it). Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Property mode set to 100644
File size: 8.9 KB
RevLine 
[7d4fbcd]1#include "owl.h"
2
[13ebf92]3static void _owl_keymap_format_bindings(const owl_keymap *km, owl_fmtext *fm);
[44cc9ab]4static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm);
[13ebf92]5
[7d4fbcd]6/* returns 0 on success */
[e19eb97]7int 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]8{
[7d4fbcd]9  if (!name || !desc) return(-1);
[fda61d3]10  km->name = g_strdup(name);
11  km->desc = g_strdup(desc);
[e4d7cb6]12  km->bindings = g_ptr_array_new();
[44cc9ab]13  km->parent = NULL;
[7d4fbcd]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! */
[0bdb3a6]21void owl_keymap_cleanup(owl_keymap *km)
[cf83b7a]22{
[ddbbcffa]23  g_free(km->name);
24  g_free(km->desc);
[3cdd6d2]25  owl_ptr_array_free(km->bindings, (GDestroyNotify)owl_keybinding_delete);
[7d4fbcd]26}
27
[44cc9ab]28void owl_keymap_set_parent(owl_keymap *km, const owl_keymap *parent)
[cf83b7a]29{
[44cc9ab]30  km->parent = parent;
[7d4fbcd]31}
32
33/* creates and adds a key binding */
[e19eb97]34int owl_keymap_create_binding(owl_keymap *km, const char *keyseq, const char *command, void (*function_fn)(void), const char *desc)
[cf83b7a]35{
[e4d7cb6]36  owl_keybinding *kb;
[7d4fbcd]37  int i;
38
[b13daa0]39  kb = owl_keybinding_new(keyseq, command, function_fn, desc);
40  if (kb == NULL)
41    return -1;
[7d4fbcd]42  /* see if another matching binding, and if so remove it.
43   * otherwise just add this one.
44   */
[e4d7cb6]45  for (i = km->bindings->len-1; i >= 0; i--) {
46    if (owl_keybinding_equal(km->bindings->pdata[i], kb)) {
47      owl_keybinding_delete(g_ptr_array_remove_index(km->bindings, i));
[7d4fbcd]48    }
49  }
[e4d7cb6]50  g_ptr_array_add(km->bindings, kb);
[d427f08]51  return 0;
[7d4fbcd]52}
53
[8a921b5]54/* removes the binding associated with the keymap */
55int owl_keymap_remove_binding(owl_keymap *km, const char *keyseq)
56{
[e4d7cb6]57  owl_keybinding *kb;
[8a921b5]58  int i;
59
[b13daa0]60  kb = owl_keybinding_new(keyseq, NULL, NULL, NULL);
61  if (kb == NULL)
62    return -1;
[8a921b5]63
[e4d7cb6]64  for (i = km->bindings->len-1; i >= 0; i--) {
65    if (owl_keybinding_equal(km->bindings->pdata[i], kb)) {
66      owl_keybinding_delete(g_ptr_array_remove_index(km->bindings, i));
[b13daa0]67      owl_keybinding_delete(kb);
[8a921b5]68      return(0);
69    }
70  }
[b13daa0]71  owl_keybinding_delete(kb);
[8a921b5]72  return(-2);
73}
74
75
[7d4fbcd]76/* returns a summary line describing this keymap.  the caller must free. */
[6829afc]77CALLER_OWN char *owl_keymap_summary(const owl_keymap *km)
[cf83b7a]78{
[7d4fbcd]79  if (!km || !km->name || !km->desc) return NULL;
[3472845]80  return g_strdup_printf("%-15s - %s", km->name, km->desc);
[7d4fbcd]81}
82
83/* Appends details about the keymap to fm */
[13ebf92]84void owl_keymap_get_details(const owl_keymap *km, owl_fmtext *fm, int recurse)
[cf83b7a]85{
[7d4fbcd]86  owl_fmtext_append_bold(fm, "KEYMAP - ");
87  owl_fmtext_append_bold(fm, km->name);
88  owl_fmtext_append_normal(fm, "\n");
89  if (km->desc) {
90    owl_fmtext_append_normal(fm, OWL_TABSTR "Purpose:    ");
91    owl_fmtext_append_normal(fm, km->desc);
92    owl_fmtext_append_normal(fm, "\n");
93  }
[44cc9ab]94  if (km->parent) {
95    owl_fmtext_append_normal(fm, OWL_TABSTR "Has parent: ");
96    owl_fmtext_append_normal(fm, km->parent->name);
[7d4fbcd]97    owl_fmtext_append_normal(fm, "\n");
98  }
99    owl_fmtext_append_normal(fm, "\n");
100  if (km->default_fn) {
101    owl_fmtext_append_normal(fm, OWL_TABSTR
102     "Has a default keypress handler (default_fn).\n");
103  }
104  if (km->prealways_fn) {
105    owl_fmtext_append_normal(fm, OWL_TABSTR
106     "Executes a function (prealways_fn) on every keypress.\n");
107  }
108  if (km->postalways_fn) {
109    owl_fmtext_append_normal(fm, OWL_TABSTR
110     "Executes a function (postalways_fn) after handling every keypress.\n");
111  }
112
113  owl_fmtext_append_bold(fm, "\nKey bindings:\n\n"); 
[13ebf92]114  if (recurse) {
[44cc9ab]115    _owl_keymap_format_with_parents(km, fm);
[13ebf92]116  } else {
117    _owl_keymap_format_bindings(km, fm);
118  }
119}
120
[44cc9ab]121static void _owl_keymap_format_with_parents(const owl_keymap *km, owl_fmtext *fm)
[13ebf92]122{
123  while (km) {
124    _owl_keymap_format_bindings(km, fm);
[44cc9ab]125    km = km->parent;
[13ebf92]126    if (km) {
127      owl_fmtext_append_bold(fm, "\nInherited from ");
128      owl_fmtext_append_bold(fm, km->name);
129      owl_fmtext_append_bold(fm, ":\n\n");
130    }
131  }
132}
133
134static void _owl_keymap_format_bindings(const owl_keymap *km, owl_fmtext *fm)
135{
[e4d7cb6]136  int i;
[13ebf92]137  const owl_keybinding *kb;
138 
[e4d7cb6]139  for (i = 0; i < km->bindings->len; i++) {
[45e2c95]140    char *kbstr;
[0a0fb74]141    const owl_cmd *cmd;
[e19eb97]142    const char *tmpdesc, *desc = "";
[7d4fbcd]143
[e4d7cb6]144    kb = km->bindings->pdata[i];
[45e2c95]145    kbstr = owl_keybinding_tostring(kb);
[7d4fbcd]146    owl_fmtext_append_normal(fm, OWL_TABSTR);
[45e2c95]147    owl_fmtext_append_normal(fm, kbstr);
148    owl_fmtext_append_spaces(fm, 11-strlen(kbstr));
149    g_free(kbstr);
[7d4fbcd]150    owl_fmtext_append_normal(fm, " - ");
151    if (kb->desc && *kb->desc) {
152      desc = kb->desc;
153    } else if ((cmd=owl_function_get_cmd(kb->command))
154               && (tmpdesc = owl_cmd_get_summary(cmd))) {
155      desc = tmpdesc;
156    }
157    owl_fmtext_append_normal(fm, desc);
158    if (kb->command && *(kb->command)) {
159      owl_fmtext_append_normal(fm, "   [");
160      owl_fmtext_append_normal(fm, kb->command);
161      owl_fmtext_append_normal(fm, "]");
162    } 
163    owl_fmtext_append_normal(fm, "\n");
164  }
165}
166
167
168
169/***********************************************************************/
170/***************************** KEYHANDLER ******************************/
171/***********************************************************************/
172
173/* NOTE: keyhandler has private access to the internals of keymap */
174
[4c7c21f]175void owl_keyhandler_init(owl_keyhandler *kh)
[cf83b7a]176{
[4c7c21f]177  owl_dict_create(&kh->keymaps);
[7d4fbcd]178  kh->active = NULL;
179  owl_keyhandler_reset(kh);
180}
181
182/* adds a new keymap */
[cf83b7a]183void owl_keyhandler_add_keymap(owl_keyhandler *kh, owl_keymap *km)
184{
[7d4fbcd]185  owl_dict_insert_element(&kh->keymaps, km->name, km, NULL);
186}
187
[e19eb97]188owl_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]189{
[7d4fbcd]190  owl_keymap *km;
[7dcef03]191  km = g_slice_new(owl_keymap);
[7d4fbcd]192  owl_keymap_init(km, name, desc, default_fn, prealways_fn, postalways_fn);
193  owl_keyhandler_add_keymap(kh, km);
194  return km;
195}
196
197/* resets state and clears out key stack */
[cf83b7a]198void owl_keyhandler_reset(owl_keyhandler *kh)
199{
[7d4fbcd]200  kh->in_esc = 0;
201  memset(kh->kpstack, 0, (OWL_KEYMAP_MAXSTACK+1)*sizeof(int));
202  kh->kpstackpos = -1;
203}
204
[12bc46a]205owl_keymap *owl_keyhandler_get_keymap(const owl_keyhandler *kh, const char *mapname)
[cf83b7a]206{
[4d86e06]207  return owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]208}
209
[ce68f23]210CALLER_OWN GPtrArray *owl_keyhandler_get_keymap_names(const owl_keyhandler *kh)
[cf83b7a]211{
[ce68f23]212  return owl_dict_get_keys(&kh->keymaps);
[7d4fbcd]213}
214
215
216/* sets the active keymap, which will also reset any key state.
217 * returns the new keymap, or NULL on failure. */
[afa200a]218const owl_keymap *owl_keyhandler_activate(owl_keyhandler *kh, const char *mapname)
[cf83b7a]219{
[afa200a]220  const owl_keymap *km;
[7d4fbcd]221  if (kh->active && !strcmp(mapname, kh->active->name)) return(kh->active);
[4d86e06]222  km = owl_dict_find_element(&kh->keymaps, mapname);
[7d4fbcd]223  if (!km) return(NULL);
224  owl_keyhandler_reset(kh);
225  kh->active = km;
226  return(km);
227}
228
229/* processes a keypress.  returns 0 if the keypress was handled,
230 * 1 if not handled, -1 on error, and -2 if j==ERR. */
[428834d]231int owl_keyhandler_process(owl_keyhandler *kh, owl_input j)
[cf83b7a]232{
[afa200a]233  const owl_keymap     *km;
[f1d7d0f]234  const owl_keybinding *kb;
[7d4fbcd]235  int i, match;
236
237  if (!kh->active) {
238    owl_function_makemsg("No active keymap!!!");
239    return(-1);
240  }
[7131cf2]241
[7d4fbcd]242  /* deal with ESC prefixing */
[428834d]243  if (!kh->in_esc && j.ch == 27) {
[7d4fbcd]244    kh->in_esc = 1;
245    return(0);
246  }
247  if (kh->in_esc) {
[428834d]248    j.ch = OWL_META(j.ch);
[7d4fbcd]249    kh->in_esc = 0;
250  }
251 
[428834d]252  kh->kpstack[++(kh->kpstackpos)] = j.ch;
[7d4fbcd]253  if (kh->kpstackpos >= OWL_KEYMAP_MAXSTACK) {
254    owl_keyhandler_reset(kh);
255    owl_function_makemsg("Too many prefix keys pressed...");
256    return(-1);
257  }
258
[44cc9ab]259  /* deal with the always_fn for the map and parents */
260  for (km=kh->active; km; km=km->parent) {
[7d4fbcd]261    if (km->prealways_fn) {
262      km->prealways_fn(j);
263    }
264  }
265
266  /* search for a match.  goes through active keymap and then
[44cc9ab]267   * through parents... TODO:  clean this up so we can pull
[7d4fbcd]268   * keyhandler and keymap apart.  */
[44cc9ab]269  for (km=kh->active; km; km=km->parent) {
[e4d7cb6]270    for (i = km->bindings->len-1; i >= 0; i--) {
271      kb = km->bindings->pdata[i];
[e1b136bf]272      match = owl_keybinding_match(kb, kh);
[7d4fbcd]273      if (match == 1) {         /* subset match */
[10b866d]274
275        /* owl_function_debugmsg("processkey: found subset match in %s", km->name); */
[7d4fbcd]276        return(0);
277      } else if (match == 2) {  /* exact match */
[10b866d]278        /* owl_function_debugmsg("processkey: found exact match in %s", km->name); */
[428834d]279        owl_keybinding_execute(kb, j.ch);
[7d4fbcd]280        owl_keyhandler_reset(kh);
281        if (km->postalways_fn) {
282          km->postalways_fn(j);
283        }
284        return(0);
285      }
286    }
287  }
288
289  /* see if a default action exists for the active keymap */
290  if (kh->active->default_fn && kh->kpstackpos<1) {
[10b866d]291    /*owl_function_debugmsg("processkey: executing default_fn");*/
292
[7d4fbcd]293    kh->active->default_fn(j);
294    owl_keyhandler_reset(kh);
295    if (kh->active->postalways_fn) {
296      kh->active->postalways_fn(j);
297    }
298    return(0);
299  }
300
301  owl_keyhandler_invalidkey(kh);
302  /* unable to handle */
303  return(1); 
304}
305
[cf83b7a]306void owl_keyhandler_invalidkey(owl_keyhandler *kh)
307{
[45e2c95]308    char *kbbuff = owl_keybinding_stack_tostring(kh->kpstack, kh->kpstackpos+1);
[7d4fbcd]309    owl_function_makemsg("'%s' is not a valid key in this context.", kbbuff);
[45e2c95]310    g_free(kbbuff);
[7d4fbcd]311    owl_keyhandler_reset(kh);
312}
Note: See TracBrowser for help on using the repository browser.