Using gtk with key tables


Useful links on this page:
  1. default key table configuration
  2. listing of gdk's key names

Now that it's all cute and all over your system, you can use key tables!

For the Casual User:

You'll probably want to be altering the way all of your keys work and changing which keys perform which actions. To keep you from killing yourself, I made a default configuration file with all of the default key bindings for gtk+-1.2.10. This file is also located in the patched gtk source under gtk/gdkkeytable_defaultrc. It is not necessary to have this file for all of this to work, but it is damned useful since I don't have a handy reference for you, right now.

Now, the way I would change my bindings is to selectively copy and paste from the default file into my ~/.gtkrc. Make sure you get the whole entry for a particular table. Here's an example of the config for the gtknotebook widget.

keytable "GtkNotebook"
{
"GtkNotebookUp" = "Up"
"GtkNotebookLeft" = "Left"
"GtkNotebookDown" = "Down"
"GtkNotebookRight" = "Right"
"GtkNotebookTabNext" = "Tab", "ISO_Left_Tab"
"GtkNotebookTabFirst" = "Home"
"GtkNotebookTabLast" = "End"
"GtkNotebookTabSel" = "Return", "space"
}

Let me warn you: yes, these names are all case sensitive. From the name of the table to the name of each action to the name of each key. I'm sorry. :) For a list of possible keys, you may want to check out gdk/gdkkeysyms.h. It *SHOULD* parse anything that's in there so long as you don't put the "GDK_" in front of each key. e.g. - 'GDK_Down' becomes 'Down'.

In an effort to help you come up with handy ways to remap keys, try replacing every instance of Up, Down, Left, Right with k, j, h, l. or <ctrl>p, <ctrl>n, <ctrl>b, <ctrl>f. I want to warn you to keep some kind of reasoning behind your bindings.... but it's your computer. :)

Want to test all of this out? Pick a widget of fun and remap its keys, now go to the examples/ directory under your gtk source tree. Find the example with that widget's name on it, make and run it. If no such example exists, feel free to run gtk/testgtk under the source tree and find your widget in the resulting test menu.


For the widget programmer

Good luck.

Having said that, let's get into the Seven(okay, Six) Habits of Key Table Using Widgets...

  1. You must include gdkkeytable.h and gtkrc.h. Like so:

    #include <gdkkeytable.h>
    #include <gtkrc.h>


  2. You must declare the key table global to your widget's class. In my case, since I am not very good at digging around in widgets and classes, I declare mine right under the declaration for parent class. (Yes, I know this is a terrible, terrible thing to do. No, I do not know if they are successfully destroyed or if they become a memory hole. Yes, I intend to fix it someday, just not now. I am documenting now.) Like so:

    static GtkWidgetClass *parent_class = NULL;
    static GdkKeyTable *calendar_key_table = NULL;


  3. Before initializing all of your defaults, I find it useful to make an enum of all the possible actions your widget will answer. This both gives you a handful of ID numbers unique to each action, as well as a significant handle for that action so your code is more readable. e.g.:
    enum {
    GTK_WINDOW_ACTIVATE_SELECTED,
    GTK_WINDOW_ACTIVATE_DEFAULT,
    GTK_WINDOW_SEL_UP,
    GTK_WINDOW_SEL_DOWN,
    GTK_WINDOW_SEL_LEFT,
    GTK_WINDOW_SEL_RIGHT,
    GTK_WINDOW_SEL_TAB_FORWARD,
    GTK_WINDOW_SEL_TAB_BACKWARD,
    };


  4. Next find the class initilization function for your widget. It will be named something cryptic like "gtk_calendar_class_init". In here you need to initialize your key table and add all of the appropriate keys and events to it so that you have some kind of functional defaults. Here's an example for gtkplug:

    plug_key_table = gdk_key_table_new();
    plug_key_table->name = g_strdup("GtkPlug"); /*very important to name your table!!!!! */
    /* adding an entry with a single key, simply use the addentry function */
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_ACTIVATE_SELECTED,
    "GtkPlugActivateSelected", 0, GDK_space);
    /* adding an entry with multiple keys requires holding on to the newly created entry. be sure to declare a temporary GdkKeyEntry and then use the addkey function on it. */
    temp_ke = gdk_key_table_addentry(plug_key_table, GTK_PLUG_ACTIVATE_DEFAULT,
    "GtkPlugActivateDefault", 0, GDK_Return);
    gdk_key_entry_addkey(temp_ke, 0, GDK_KP_Enter);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_UP,
    "GtkPlugDirUp", 0, GDK_Up);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_DOWN,
    "GtkPlugDirDown", 0, GDK_Down);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_LEFT,
    "GtkPlugDirLeft", 0, GDK_Left);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_RIGHT,
    "GtkPlugDirRight", 0, GDK_Right);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_TAB_FORWARD,
    "GtkPlugDirTabForward", 0, GDK_Tab);
    gdk_key_table_addentry(plug_key_table, GTK_PLUG_DIR_TAB_BACKWARD,
    "GtkPlugDirTabBackward", GDK_SHIFT_MASK, GDK_Tab);
    /* Now that our table has been defined, let gtkrc put the user's changes in it */
    gtk_rc_keytable_set_from_config(plug_key_table);


  5. Having configured the defaults for the table, it's time to put it to use. In your gtk_[widgetnameofchoice]_key_press function, or whatever it is you have bound to the key_press_event signal or some such, do something like the following:
    guint thekey;

    thekey = gdk_key_table_getaction(plug_key_table, event);
    /* where 'plug_key_table' is the name of your keytable, and 'event' is the incoming GdkEventKey passed with the key_press signal */

    switch(thekey){
    case ENUMERATED_THING_1:
    ...
    break;
    case ENUMERATED_THING_2:
    ...
    etc.
    ...
    };


  6. That's it! It oughta work. The only thing left is to garbage collect your key table when the widget class dies (ie - the program ends). If you put it someplace better than Right Out In the Open, like me, it should be easy. Then again, maybe each widget has a destruction function I could slip it in? Bah. I am documenting, not thinking!