]> Dogcows Code - chaz/openbox/commitdiff
add keyboard shortcuts to the menus. you can specify the shortcut key with & even...
authorDana Jansens <danakj@orodu.net>
Wed, 25 Apr 2007 01:33:20 +0000 (01:33 +0000)
committerDana Jansens <danakj@orodu.net>
Wed, 25 Apr 2007 01:33:20 +0000 (01:33 +0000)
openbox/client_menu.c
openbox/event.c
openbox/menu.c
openbox/menu.h
openbox/menuframe.c
openbox/menuframe.h
openbox/translate.c
openbox/translate.h

index f67ea906dd7280d0a5dcc88444791847f7c67444..fc88025cdb47e71211ae7c3d29082930eb14a1ec 100644 (file)
@@ -70,16 +70,14 @@ static void client_update(ObMenuFrame *frame, gpointer data)
     e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_ICONIFY;
 
     e = menu_find_entry_id(menu, CLIENT_MAXIMIZE);
-    g_free(e->data.normal.label);
-    e->data.normal.label =
-        g_strdup(frame->client->max_vert || frame->client->max_horz ?
-                 _("Restore") : _("Maximize"));
+    menu_entry_set_label(e,
+                         (frame->client->max_vert || frame->client->max_horz ?
+                          _("Restor&e") : _("Maximiz&e")));
     e->data.normal.enabled =frame->client->functions & OB_CLIENT_FUNC_MAXIMIZE;
 
     e = menu_find_entry_id(menu, CLIENT_SHADE);
-    g_free(e->data.normal.label);
-    e->data.normal.label = g_strdup(frame->client->shaded ?
-                                    _("Roll down") : _("Roll up"));
+    menu_entry_set_label(e, (frame->client->shaded ?
+                             _("&Roll down") : _("&Roll up")));
     e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_SHADE;
 
     e = menu_find_entry_id(menu, CLIENT_MOVE);
@@ -165,29 +163,32 @@ void client_menu_startup()
     ObMenu *menu;
     ObMenuEntry *e;
 
-    menu = menu_new(LAYER_MENU_NAME, _("Layer"), NULL);
+    menu = menu_new(LAYER_MENU_NAME, _("&Layer"), NULL);
+    menu_show_all_shortcuts(menu, TRUE);
     menu_set_update_func(menu, layer_update);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("SendToTopLayer", OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, LAYER_TOP, _("Always on top"), acts);
+    menu_add_normal(menu, LAYER_TOP, _("Always on &top"), acts);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("SendToNormalLayer",
                             OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, LAYER_NORMAL, _("Normal"), acts);
+    menu_add_normal(menu, LAYER_NORMAL, _("&Normal"), acts);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("SendToBottomLayer",
                             OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, LAYER_BOTTOM, _("Always on bottom"),acts);
+    menu_add_normal(menu, LAYER_BOTTOM, _("Always on &bottom"),acts);
 
 
-    menu = menu_new(SEND_TO_MENU_NAME, _("Send to desktop"), NULL);
+    menu = menu_new(SEND_TO_MENU_NAME, _("&Send to desktop"), NULL);
+    menu_show_all_shortcuts(menu, TRUE);
     menu_set_update_func(menu, send_to_update);
 
 
     menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), NULL);
+    menu_show_all_shortcuts(menu, TRUE);
     menu_set_update_func(menu, client_update);
 
     menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME);
@@ -196,7 +197,7 @@ void client_menu_startup()
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Iconify", OB_USER_ACTION_MENU_SELECTION));
-    e = menu_add_normal(menu, CLIENT_ICONIFY, _("Iconify"), acts);
+    e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico&nify"), acts);
     e->data.normal.mask = ob_rr_theme->iconify_mask;
     e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
     e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
@@ -213,11 +214,11 @@ void client_menu_startup()
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Raise", OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, CLIENT_RAISE, _("Raise to top"), acts);
+    menu_add_normal(menu, CLIENT_RAISE, _("Raise to &top"), acts);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Lower", OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, CLIENT_LOWER, _("Lower to bottom"),acts);
+    menu_add_normal(menu, CLIENT_LOWER, _("Lower to &bottom"),acts);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("ToggleShade", OB_USER_ACTION_MENU_SELECTION));
@@ -230,23 +231,23 @@ void client_menu_startup()
     acts = g_slist_prepend(NULL, action_from_string
                            ("ToggleDecorations",
                             OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, CLIENT_DECORATE, _("Decorate"), acts);
+    menu_add_normal(menu, CLIENT_DECORATE, _("&Decorate"), acts);
 
     menu_add_separator(menu, -1, NULL);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Move", OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, CLIENT_MOVE, _("Move"), acts);
+    menu_add_normal(menu, CLIENT_MOVE, _("&Move"), acts);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Resize", OB_USER_ACTION_MENU_SELECTION));
-    menu_add_normal(menu, CLIENT_RESIZE, _("Resize"), acts);
+    menu_add_normal(menu, CLIENT_RESIZE, _("&Resize"), acts);
 
     menu_add_separator(menu, -1, NULL);
 
     acts = g_slist_prepend(NULL, action_from_string
                            ("Close", OB_USER_ACTION_MENU_SELECTION));
-    e = menu_add_normal(menu, CLIENT_CLOSE, _("Close"), acts);
+    e = menu_add_normal(menu, CLIENT_CLOSE, _("&Close"), acts);
     e->data.normal.mask = ob_rr_theme->close_mask;
     e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
     e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
index 788fd4bc1f128d5291bd148b079ac01009b00a89..9d6ff5fd86f09d8ee9ac24326d84411c10a98347 100644 (file)
@@ -39,6 +39,7 @@
 #include "group.h"
 #include "stacking.h"
 #include "extensions.h"
+#include "translate.h"
 
 #include <X11/Xlib.h>
 #include <X11/keysym.h>
@@ -72,6 +73,7 @@ typedef struct
 
 static void event_process(const XEvent *e, gpointer data);
 static void event_handle_root(XEvent *e);
+static void event_handle_menu_shortcut(XEvent *e);
 static void event_handle_menu(XEvent *e);
 static void event_handle_dock(ObDock *s, XEvent *e);
 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
@@ -1262,7 +1264,7 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e)
     }
 }
 
-ObMenuFrame* find_active_menu()
+static ObMenuFrame* find_active_menu()
 {
     GList *it;
     ObMenuFrame *ret = NULL;
@@ -1276,7 +1278,7 @@ ObMenuFrame* find_active_menu()
     return ret;
 }
 
-ObMenuFrame* find_active_or_last_menu()
+static ObMenuFrame* find_active_or_last_menu()
 {
     ObMenuFrame *ret = NULL;
 
@@ -1286,6 +1288,77 @@ ObMenuFrame* find_active_or_last_menu()
     return ret;
 }
 
+static void event_handle_menu_shortcut(XEvent *ev)
+{
+    gunichar unikey = 0;
+    ObMenuFrame *frame;
+    GList *start;
+    GList *it;
+    ObMenuEntryFrame *found = NULL;
+    guint num_found = 0;
+
+    {
+        const char *key;
+        if ((key = translate_keycode(ev->xkey.keycode)) == NULL)
+            return;
+        unikey = g_utf8_get_char_validated(key, -1);
+        if (unikey == (gunichar)-1 || unikey == (gunichar)-2 || unikey == 0)
+            return;
+    }
+
+    if ((frame = find_active_or_last_menu()) == NULL)
+        return;
+
+
+    if (!frame->entries)
+        return; /* nothing in the menu anyways */
+
+    /* start after the selected one */
+    start = frame->entries;
+    if (frame->selected) {
+        for (it = start; frame->selected != it->data; it = g_list_next(it))
+            g_assert(it != NULL); /* nothing was selected? */
+        /* next with wraparound */
+        start = g_list_next(it);
+        if (start == NULL) start = frame->entries;
+    }
+
+    it = start;
+    do {
+        ObMenuEntryFrame *e = it->data;
+        gunichar entrykey = 0;
+
+        if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+            entrykey = e->entry->data.normal.shortcut;
+        else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+            entrykey = e->entry->data.submenu.submenu->shortcut;
+
+        if (unikey == entrykey) {
+            if (found == NULL) found = e;
+            ++num_found;
+        }
+
+        /* next with wraparound */
+        it = g_list_next(it);
+        if (it == NULL) it = frame->entries;
+    } while (it != start);
+
+    if (found) {
+        if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+            num_found == 1)
+        {
+            menu_frame_select(frame, found, TRUE);
+            usleep(50000);
+            menu_entry_frame_execute(found, ev->xkey.state,
+                                     ev->xkey.time);
+        } else {
+            menu_frame_select(frame, found, TRUE);
+            if (num_found == 1)
+                menu_frame_select_next(frame->child);
+        }
+    }
+}
+
 static void event_handle_menu(XEvent *ev)
 {
     ObMenuFrame *f;
@@ -1307,7 +1380,7 @@ static void event_handle_menu(XEvent *ev)
             if (e->ignore_enters)
                 --e->ignore_enters;
             else
-                menu_frame_select(e->frame, e);
+                menu_frame_select(e->frame, e, FALSE);
         }
         break;
     case LeaveNotify:
@@ -1315,28 +1388,35 @@ static void event_handle_menu(XEvent *ev)
             (f = find_active_menu()) && f->selected == e &&
             e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
         {
-            menu_frame_select(e->frame, NULL);
+            menu_frame_select(e->frame, NULL, FALSE);
         }
     case MotionNotify:   
         if ((e = menu_entry_frame_under(ev->xmotion.x_root,   
                                         ev->xmotion.y_root)))
-            menu_frame_select(e->frame, e);   
+            menu_frame_select(e->frame, e, FALSE);
         break;
     case KeyPress:
         if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
-            menu_frame_hide_all();
+            if ((f = find_active_or_last_menu()) && f->parent)
+                menu_frame_select(f, NULL, TRUE);
+            else
+                menu_frame_hide_all();
         else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
             ObMenuFrame *f;
-            if ((f = find_active_menu()))
-                menu_entry_frame_execute(f->selected, ev->xkey.state,
-                                         ev->xkey.time);
+            if ((f = find_active_menu())) {
+                if (f->child)
+                    menu_frame_select_next(f->child);
+                else
+                    menu_entry_frame_execute(f->selected, ev->xkey.state,
+                                             ev->xkey.time);
+            }
         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
             ObMenuFrame *f;
-            if ((f = find_active_or_last_menu()) && f->parent)
-                menu_frame_select(f, NULL);
+            if ((f = find_active_or_last_menu()))
+                menu_frame_select(f, NULL, TRUE);
         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
             ObMenuFrame *f;
-            if ((f = find_active_or_last_menu()) && f->child)
+            if ((f = find_active_menu()) && f->child)
                 menu_frame_select_next(f->child);
         } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
             ObMenuFrame *f;
@@ -1346,7 +1426,8 @@ static void event_handle_menu(XEvent *ev)
             ObMenuFrame *f;
             if ((f = find_active_or_last_menu()))
                 menu_frame_select_next(f);
-        }
+        } else
+            event_handle_menu_shortcut(ev);
         break;
     }
 }
index 311701a7aa33cd5a4bd5d45474dc0d7356fee780..1f335c787dac724b26bc3b0a97c3f964b4cf861d 100644 (file)
@@ -54,6 +54,9 @@ static void parse_menu_separator(ObParseInst *i,
                                  gpointer data);
 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
                        gpointer data);
+static gunichar parse_shortcut(const gchar *label, gchar **strippedlabel,
+                               guint *position);
+
 
 static void client_dest(ObClient *client, gpointer data)
 {
@@ -178,6 +181,56 @@ static ObMenu* menu_from_name(gchar *name)
     return self;
 }  
 
+#define VALID_SHORTCUT(c) (((c) >= '0' && (c) <= '9') || \
+                           ((c) >= 'A' && (c) <= 'Z') || \
+                           ((c) >= 'a' && (c) <= 'z'))
+
+static gunichar parse_shortcut(const gchar *label, gchar **strippedlabel,
+                               guint *position)
+{
+    gunichar shortcut = 0;
+    
+    *position = 0;
+
+    g_assert(strippedlabel != NULL);
+
+    if (label == NULL) {
+        *strippedlabel = NULL;
+    } else {
+        gchar *i;
+
+        *strippedlabel = g_strdup(label);
+
+        i = strchr(*strippedlabel, '&');
+        if (i != NULL) {
+            /* there is an ampersand in the string */
+
+            /* you have to use a printable ascii character for shortcuts
+               don't allow space either, so you can have like "a & b"
+            */
+            if (VALID_SHORTCUT(*(i+1))) {
+                shortcut = g_unichar_tolower(g_utf8_get_char(i+1));
+                *position = i - *strippedlabel;
+
+                /* remove the & from the string */
+                for (; *i != '\0'; ++i)
+                    *i = *(i+1);
+            }
+        } else {
+            /* there is no ampersand, so find the first valid character to use
+               instead */
+
+            for (i = *strippedlabel; *i != '\0'; ++i)
+                if (VALID_SHORTCUT(*i)) {
+                    *position = i - *strippedlabel;
+                    shortcut = g_unichar_tolower(g_utf8_get_char(i));
+                    break;
+                }
+        }
+    }
+    return shortcut;
+}
+
 static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
                             gpointer data)
 {
@@ -262,9 +315,11 @@ ObMenu* menu_new(const gchar *name, const gchar *title, gpointer data)
 
     self = g_new0(ObMenu, 1);
     self->name = g_strdup(name);
-    self->title = g_strdup(title);
     self->data = data;
 
+    self->shortcut = parse_shortcut(title, &self->title,
+                                    &self->shortcut_position);
+
     g_hash_table_replace(menu_hash, self->name, self);
 
     return self;
@@ -325,7 +380,7 @@ void menu_show(gchar *name, gint x, gint y, ObClient *client)
         ObMenuEntryFrame *e = frame->entries->data;
         if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
             e->entry->data.normal.enabled)
-                menu_frame_select(frame, e);
+                menu_frame_select(frame, e, FALSE);
     }
 }
 
@@ -409,9 +464,10 @@ ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
     ObMenuEntry *e;
 
     e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
-    e->data.normal.label = g_strdup(label);
     e->data.normal.actions = actions;
 
+    menu_entry_set_label(e, label);
+
     self->entries = g_list_append(self->entries, e);
     return e;
 }
@@ -432,7 +488,8 @@ ObMenuEntry* menu_add_separator(ObMenu *self, gint id, const gchar *label)
     ObMenuEntry *e;
 
     e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
-    e->data.separator.label = g_strdup(label);
+
+    menu_entry_set_label(e, label);
 
     self->entries = g_list_append(self->entries, e);
     return e;
@@ -480,3 +537,26 @@ void menu_find_submenus(ObMenu *self)
             e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
     }
 }
+
+void menu_entry_set_label(ObMenuEntry *self, const gchar *label)
+{
+    switch (self->type) {
+    case OB_MENU_ENTRY_TYPE_SEPARATOR:
+        g_free(self->data.separator.label);
+        self->data.separator.label = g_strdup(label);
+        break;
+    case OB_MENU_ENTRY_TYPE_NORMAL:
+        g_free(self->data.normal.label);
+        self->data.normal.shortcut =
+            parse_shortcut(label, &self->data.normal.label,
+                           &self->data.normal.shortcut_position);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+void menu_show_all_shortcuts(ObMenu *self, gboolean show)
+{
+    self->show_all_shortcuts = show;
+}
index 5b8810fd255e15683bc8ddc1abe471d7cc48cc65..bdc05266b95b32f4111ac2bc2354b016153d7c6e 100644 (file)
@@ -48,6 +48,15 @@ struct _ObMenu
     gchar *name;
     /* Displayed title */
     gchar *title;
+    /*! The shortcut key that would be used to activate this menu if it was
+      displayed as a submenu */
+    gunichar shortcut;
+    /*! The shortcut's position in the string */
+    guint shortcut_position;
+
+    /*! If the shortcut key should be shown in menu entries even when it
+      is the first character in the string */
+    gboolean show_all_shortcuts;
 
     /* Command to execute to rebuild the menu */
     gchar *execute;
@@ -75,6 +84,10 @@ typedef enum
 
 struct _ObNormalMenuEntry {
     gchar *label;
+    /*! The shortcut key that would be used to activate this menu entry */
+    gunichar shortcut;
+    /*! The shortcut's position in the string */
+    guint shortcut_position;
 
     /* state */
     gboolean enabled;
@@ -126,6 +139,8 @@ void menu_free(ObMenu *menu);
 /* Repopulate a pipe-menu by running its command */
 void menu_pipe_execute(ObMenu *self);
 
+void menu_show_all_shortcuts(ObMenu *self, gboolean show);
+
 void menu_show(gchar *name, gint x, gint y, struct _ObClient *client);
 
 void menu_set_update_func(ObMenu *menu, ObMenuUpdateFunc func);
@@ -141,6 +156,8 @@ ObMenuEntry* menu_add_separator(ObMenu *menu, gint id, const gchar *label);
 void menu_clear_entries(ObMenu *menu);
 void menu_entry_remove(ObMenuEntry *self);
 
+void menu_entry_set_label(ObMenuEntry *self, const gchar *label);
+
 ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id);
 
 /* fills in the submenus, for use when a menu is being shown */
index 4cb2083dc1c7294b4d73114d322a136594beea22..d92f47df686cc756dc7ae635dafe23e0d522cee6 100644 (file)
@@ -318,6 +318,13 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
                    self->a_text_selected :
                    self->a_text_normal));
         text_a->texture[0].data.text.string = self->entry->data.normal.label;
+        if (self->frame->menu->show_all_shortcuts ||
+            self->entry->data.normal.shortcut_position > 0)
+        {
+            text_a->texture[0].data.text.shortcut =
+                self->entry->data.normal.shortcut;
+        } else
+            text_a->texture[0].data.text.shortcut = 0;
         break;
     case OB_MENU_ENTRY_TYPE_SUBMENU:
         text_a = (self == self->frame->selected ?
@@ -325,6 +332,11 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
                   self->a_text_normal);
         sub = self->entry->data.submenu.submenu;
         text_a->texture[0].data.text.string = sub ? sub->title : "";
+        if (self->frame->menu->show_all_shortcuts ||
+            sub->shortcut_position > 0) {
+            text_a->texture[0].data.text.shortcut = sub->shortcut;
+        } else
+            text_a->texture[0].data.text.shortcut = 0;
         break;
     case OB_MENU_ENTRY_TYPE_SEPARATOR:
         if (self->entry->data.separator.label != NULL)
@@ -886,7 +898,8 @@ static gboolean menu_entry_frame_submenu_timeout(gpointer data)
     return FALSE;
 }
 
-void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
+void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
+                       gboolean immediate)
 {
     ObMenuEntryFrame *old = self->selected;
     ObMenuFrame *oldchild = self->child;
@@ -913,7 +926,7 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
         menu_entry_frame_render(self->selected);
 
         if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
-            if (config_submenu_show_delay) {
+            if (config_submenu_show_delay && !immediate) {
                 /* initiate a new submenu open request */
                 ob_main_loop_timeout_add(ob_main_loop,
                                          config_submenu_show_delay * 1000,
@@ -988,7 +1001,7 @@ void menu_frame_select_previous(ObMenuFrame *self)
             }
         }
     }
-    menu_frame_select(self, it ? it->data : NULL);
+    menu_frame_select(self, it ? it->data : NULL, TRUE);
 }
 
 void menu_frame_select_next(ObMenuFrame *self)
@@ -1014,5 +1027,5 @@ void menu_frame_select_next(ObMenuFrame *self)
             }
         }
     }
-    menu_frame_select(self, it ? it->data : NULL);
+    menu_frame_select(self, it ? it->data : NULL, TRUE);
 }
index 016700a74ae7e6fad32967f1916cf05aa2eab939..4cd27d3767c74f767c2392b0032c9abcd70bf028 100644 (file)
@@ -124,7 +124,8 @@ void menu_frame_hide(ObMenuFrame *self);
 void menu_frame_hide_all();
 void menu_frame_hide_all_client(struct _ObClient *client);
 
-void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry);
+void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
+                       gboolean immediate);
 void menu_frame_select_previous(ObMenuFrame *self);
 void menu_frame_select_next(ObMenuFrame *self);
 
index a26017e512065fe2b5926dcea603760e578a40b6..97066519f4b9f0cf73e53826e77a59e398cf5777 100644 (file)
@@ -139,3 +139,13 @@ translation_fail:
     g_strfreev(parsed);
     return ret;
 }
+
+const gchar *translate_keycode(guint keycode)
+{
+    KeySym sym;
+    const gchar *ret = NULL;
+
+    if ((sym = XKeycodeToKeysym(ob_display, keycode, 0)) != NoSymbol)
+        ret = XKeysymToString(sym);
+    return g_locale_to_utf8(ret, -1, NULL, NULL, NULL);
+}
index 8249514e10e5fdd47051f4abe96a1fe5f2aa6cda..14efe73da6a64688a8904338b25f7e4b6695e916 100644 (file)
@@ -24,4 +24,6 @@
 gboolean translate_button(const gchar *str, guint *state, guint *keycode);
 gboolean translate_key(const gchar *str, guint *state, guint *keycode);
 
+/*! Give the string form of a keycode */
+const gchar *translate_keycode(guint keycode);
 #endif
This page took 0.04095 seconds and 4 git commands to generate.