source: keymap.c @ 5738650

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