add the missing files for the keyedit branch
authorDana Jansens <danakj@orodu.net>
Mon, 30 Jul 2007 16:37:38 +0000 (12:37 -0400)
committerDana Jansens <danakj@orodu.net>
Thu, 24 Jan 2008 16:23:46 +0000 (11:23 -0500)
src/actions.c [new file with mode: 0644]
src/actions.h [new file with mode: 0644]
src/keyboard.c [new file with mode: 0644]
src/keyboard.h [new file with mode: 0644]

diff --git a/src/actions.c b/src/actions.c
new file mode 100644 (file)
index 0000000..198ddd3
--- /dev/null
@@ -0,0 +1,363 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   actions.c for ObConf, the configuration tool for Openbox
+   Copyright (c) 2007        Justin Stallard
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "actions.h"
+
+gchar * action_option_get_text_from_name(Option name)
+{
+   switch (name)
+   {
+        case O_EXECUTE:
+            return g_strdup("execute");
+            break;
+        case O_MENU:
+            return g_strdup("menu");
+            break;
+        case O_DIALOG:
+            return g_strdup("dialog");
+            break;
+        case O_ALL_DESKTOPS:
+            return g_strdup("allDesktops");
+            break;
+        case O_PANELS:
+            return g_strdup("panels");
+            break;
+        case O_INCLUDE_DESKTOP:
+            return g_strdup("desktop");
+            break;
+        case O_LINEAR:
+            return g_strdup("linear");
+            break;
+        case O_DESKTOP:
+            return g_strdup("desktop");
+            break;
+        case O_WRAP:
+            return g_strdup("wrap");
+            break;
+        case O_STRING:
+            return g_strdup("string");
+            break;
+        case O_FOLLOW:
+            return g_strdup("follow");
+            break;
+        case O_EDGE:
+            return g_strdup("edge");
+            break;
+        case O_X:
+            return g_strdup("x");
+            break;
+        case O_Y:
+            return g_strdup("y");
+            break;
+        case O_LEFT:
+            return g_strdup("left");
+            break;
+        case O_RIGHT:
+            return g_strdup("right");
+            break;
+        case O_UP:
+            return g_strdup("up");
+            break;
+        case O_DOWN:
+            return g_strdup("down");
+            break;
+        case O_NUM_OPTIONS:
+        default:
+            return NULL;
+            break;
+    }
+}
+
+Option action_option_get_name_from_text(const gchar *act, const gchar *opt)
+{
+    gint i;
+    gchar *txt;
+
+    if (!strcmp(opt, "desktop"))
+    {
+        if (!strcmp(act, "NextWindow") || !strcmp(act, "PreviousWindow"))
+        {
+            return O_INCLUDE_DESKTOP;
+        }
+        else
+        {
+            return O_DESKTOP;
+        }
+    }
+
+    for (i = 0; i < O_NUM_OPTIONS; i++)
+    {
+        if (i == O_INCLUDE_DESKTOP || i == O_DESKTOP)
+            continue;
+
+        txt = action_option_get_text_from_name(i);
+
+        if (!strcmp(opt, txt))
+        {
+            g_free(txt);
+            return i;
+        }
+        g_free(txt);
+    }
+
+    return -1;
+}
+
+GType action_option_get_type_from_text(const gchar *act, const gchar *opt)
+{
+    Option nopt;
+
+    nopt = action_option_get_name_from_text(act, opt);
+
+    switch (nopt)
+    {
+        case O_EXECUTE:
+        case O_MENU:
+        case O_STRING:
+        case O_EDGE:
+            return G_TYPE_STRING;
+            break;
+        case O_DIALOG:
+        case O_ALL_DESKTOPS:
+        case O_PANELS:
+        case O_INCLUDE_DESKTOP:
+        case O_LINEAR:
+        case O_WRAP:
+        case O_FOLLOW:
+            return G_TYPE_BOOLEAN;
+            break;
+        case O_DESKTOP:
+        case O_X:
+        case O_Y:
+        case O_LEFT:
+        case O_RIGHT:
+        case O_UP:
+        case O_DOWN:
+            return G_TYPE_INT;
+            break;
+        case O_NUM_OPTIONS:
+        default:
+            return -1;
+            break;
+    }
+}
+
+gchar * action_get_text_from_name(Action name)
+{
+    switch (name)
+    {
+        case A_EXECUTE:
+            return g_strdup("Execute");
+        case A_SHOW_MENU:
+            return g_strdup("ShowMenu");
+        case A_NEXT_WINDOW:
+            return g_strdup("NextWindow");
+        case A_PREVIOUS_WINDOW:
+            return g_strdup("PreviousWindow");
+        case A_DIRECTIONAL_FOCUS_NORTH:
+            return g_strdup("DirectionalFocusNorth");
+        case A_DIRECTIONAL_FOCUS_SOUTH:
+            return g_strdup("DirectionalFocusSouth");
+        case A_DIRECTIONAL_FOCUS_EAST:
+            return g_strdup("DirectionalFocusEast");
+        case A_DIRECTIONAL_FOCUS_WEST:
+            return g_strdup("DirectionalFocusWest");
+        case A_DIRECTIONAL_FOCUS_NORTH_WEST:
+            return g_strdup("DirectionalFocusNorthWest");
+        case A_DIRECTIONAL_FOCUS_NORTH_EAST:
+            return g_strdup("DirectionalFocusNorthEast");
+        case A_DIRECTIONAL_FOCUS_SOUTH_WEST:
+            return g_strdup("DirectionalFocusSouthWest");
+        case A_DIRECTIONAL_FOCUS_SOUTH_EAST:
+            return g_strdup("DirectionalFocusSouthEast");
+        case A_DESKTOP:
+            return g_strdup("Desktop");
+        case A_DESKTOP_NEXT:
+            return g_strdup("DesktopNext");
+        case A_DESKTOP_PREVIOUS:
+            return g_strdup("DesktopPrevious");
+        case A_DESKTOP_LEFT:
+            return g_strdup("DesktopLeft");
+        case A_DESKTOP_RIGHT:
+            return g_strdup("DesktopRight");
+        case A_DESKTOP_UP:
+            return g_strdup("DesktopUp");
+        case A_DESKTOP_DOWN:
+            return g_strdup("DesktopDown");
+        case A_DESKTOP_LAST:
+            return g_strdup("DesktopLast");
+        case A_ADD_DESKTOP_LAST:
+            return g_strdup("AddDesktopLast");
+        case A_REMOVE_DESKTOP_LAST:
+            return g_strdup("RemoveDesktopLast");
+        case A_ADD_DESKTOP_CURRENT:
+            return g_strdup("AddDesktopCurrent");
+        case A_REMOVE_DESKTOP_CURRENT:
+            return g_strdup("RemoveDesktopCurrent");
+        case A_TOGGLE_SHOW_DESKTOP:
+            return g_strdup("ToggleShowDesktop");
+        case A_SHOW_DESKTOP:
+            return g_strdup("ShowDesktop");
+        case A_UNSHOW_DESKTOP:
+            return g_strdup("UnshowDesktop");
+        case A_TOGGLE_DOCK_AUTOHIDE:
+            return g_strdup("ToggleDockAutohide");
+        case A_RECONFIGURE:
+            return g_strdup("Reconfigure");
+        case A_RESTART:
+            return g_strdup("Restart");
+        case A_EXIT:
+            return g_strdup("Exit");
+        case A_DEBUG:
+            return g_strdup("Debug");
+        case A_ACTIVATE:
+            return g_strdup("Activate");
+        case A_FOCUS:
+            return g_strdup("Focus");
+        case A_RAISE:
+            return g_strdup("Raise");
+        case A_LOWER:
+            return g_strdup("Lower");
+        case A_RAISE_LOWER:
+            return g_strdup("RaiseLower");
+        case A_UNFOCUS:
+            return g_strdup("Unfocus");
+        case A_FOCUS_TO_BOTTOM:
+            return g_strdup("FocusToBottom");
+        case A_ICONIFY:
+            return g_strdup("Iconify");
+        case A_CLOSE:
+            return g_strdup("Close");
+        case A_TOGGLE_SHADE:
+            return g_strdup("ToggleShade");
+        case A_SHADE:
+            return g_strdup("Shade");
+        case A_UNSHADE:
+            return g_strdup("Unshade");
+        case A_TOGGLE_OMNIPRESENT:
+            return g_strdup("ToggleOmnipresent");
+        case A_TOGGLE_MAXIMIZE_FULL:
+            return g_strdup("ToggleMaximizeFull");
+        case A_MAXIMIZE_FULL:
+            return g_strdup("MaximizeFull");
+        case A_UNMAXIMIZE_FULL:
+            return g_strdup("UnmaximizeFull");
+        case A_TOGGLE_MAXIMIZE_VERT:
+            return g_strdup("ToggleMaximizeVert");
+        case A_MAXIMIZE_VERT:
+            return g_strdup("MaximizeVert");
+        case A_UNMAXIMIZE_VERT:
+            return g_strdup("UnmaximizeVert");
+        case A_TOGGLE_MAXIMIZE_HORZ:
+            return g_strdup("ToggleMaximizeHorz");
+        case A_MAXIMIZE_HORZ:
+            return g_strdup("MaximizeHorz");
+        case A_UNMAXIMIZE_HORZ:
+            return g_strdup("UnmaximizeHorz");
+        case A_TOGGLE_FULLSCREEN:
+            return g_strdup("ToggleFullscreen");
+        case A_TOGGLE_DECORATIONS:
+            return g_strdup("ToggleDecorations");
+        case A_SEND_TO_DESKTOP:
+            return g_strdup("SendToDesktop");
+        case A_SEND_TO_DESKTOP_NEXT:
+            return g_strdup("SendToDesktopNext");
+        case A_SEND_TO_DESKTOP_PREVIOUS:
+            return g_strdup("SendToDesktopPrevious");
+        case A_SEND_TO_DESKTOP_LEFT:
+            return g_strdup("SendToDesktopLeft");
+        case A_SEND_TO_DESKTOP_RIGHT:
+            return g_strdup("SendToDesktopRight");
+        case A_SEND_TO_DESKTOP_UP:
+            return g_strdup("SendToDesktopUp");
+        case A_SEND_TO_DESKTOP_DOWN:
+            return g_strdup("SendToDesktopDown");
+        case A_MOVE:
+            return g_strdup("Move");
+        case A_RESIZE:
+            return g_strdup("Resize");
+        case A_MOVE_TO_CENTER:
+            return g_strdup("MoveToCenter");
+        case A_MOVE_RELATIVE:
+            return g_strdup("MoveRelative");
+        case A_RESIZE_RELATIVE:
+            return g_strdup("ResizeRelative");
+        case A_MOVE_TO_EDGE_NORTH:
+            return g_strdup("MoveToEdgeNorth");
+        case A_MOVE_TO_EDGE_SOUTH:
+            return g_strdup("MoveToEdgeSouth");
+        case A_MOVE_TO_EDGE_WEST:
+            return g_strdup("MoveToEdgeWest");
+        case A_MOVE_TO_EDGE_EAST:
+            return g_strdup("MoveToEdgeEast");
+        case A_MOVE_FROM_EDGE_NORTH:
+            return g_strdup("MoveFromEdgeNorth");
+        case A_MOVE_FROM_EDGE_SOUTH:
+            return g_strdup("MoveFromEdgeSouth");
+        case A_MOVE_FROM_EDGE_WEST:
+            return g_strdup("MoveFromEdgeWest");
+        case A_MOVE_FROM_EDGE_EAST:
+            return g_strdup("MoveFromEdgeEast");
+        case A_GROW_TO_EDGE_NORTH:
+            return g_strdup("GrowToEdgeNorth");
+        case A_GROW_TO_EDGE_SOUTH:
+            return g_strdup("GrowToEdgeSouth");
+        case A_GROW_TO_EDGE_WEST:
+            return g_strdup("GrowToEdgeWest");
+        case A_GROW_TO_EDGE_EAST:
+            return g_strdup("GrowToEdgeEast");
+        case A_SHADE_LOWER:
+            return g_strdup("ShadeLower");
+        case A_UNSHADE_RAISE:
+            return g_strdup("UnshadeRaise");
+        case A_TOGGLE_ALWAYS_ON_TOP:
+            return g_strdup("ToggleAlwaysOnTop");
+        case A_TOGGLE_ALWAYS_ON_BOTTOM:
+            return g_strdup("ToggleAlwaysOnBottom");
+        case A_SEND_TO_TOP_LAYER:
+            return g_strdup("SendToTopLayer");
+        case A_SEND_TO_BOTTOM_LAYER:
+            return g_strdup("SendToBottomLayer");
+        case A_SEND_TO_NORMAL_LAYER:
+            return g_strdup("SendToNormalLayer");
+
+        case NUM_ACTIONS:
+            return NULL;
+    }
+
+    return NULL;
+}
+
+Action action_get_name_from_text(const gchar *text)
+{
+    gint i;
+    gchar *tmp;
+
+    for (i = 0; i < NUM_ACTIONS; i++)
+    {
+        tmp = action_get_text_from_name(i);
+        if (!strcmp(text, tmp))
+        {
+            g_free(tmp);
+            return i;
+        }
+        g_free(tmp);
+    }
+
+    return -1;
+}
diff --git a/src/actions.h b/src/actions.h
new file mode 100644 (file)
index 0000000..dfd1547
--- /dev/null
@@ -0,0 +1,145 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   actions.h for ObConf, the configuration tool for Openbox
+   Copyright (c) 2007        Justin Stallard
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef obconf__actions_h
+#define obconf__actions_h
+
+#include <gtk/gtk.h>
+
+typedef enum {
+    A_EXECUTE,
+    A_SHOW_MENU,
+    A_NEXT_WINDOW,
+    A_PREVIOUS_WINDOW,
+    A_DIRECTIONAL_FOCUS_NORTH,
+    A_DIRECTIONAL_FOCUS_SOUTH,
+    A_DIRECTIONAL_FOCUS_EAST,
+    A_DIRECTIONAL_FOCUS_WEST,
+    A_DIRECTIONAL_FOCUS_NORTH_WEST,
+    A_DIRECTIONAL_FOCUS_NORTH_EAST,
+    A_DIRECTIONAL_FOCUS_SOUTH_WEST,
+    A_DIRECTIONAL_FOCUS_SOUTH_EAST,
+    A_DESKTOP,
+    A_DESKTOP_NEXT,
+    A_DESKTOP_PREVIOUS,
+    A_DESKTOP_LEFT,
+    A_DESKTOP_RIGHT,
+    A_DESKTOP_UP,
+    A_DESKTOP_DOWN,
+    A_DESKTOP_LAST,
+    A_ADD_DESKTOP_LAST,
+    A_REMOVE_DESKTOP_LAST,
+    A_ADD_DESKTOP_CURRENT,
+    A_REMOVE_DESKTOP_CURRENT,
+    A_TOGGLE_SHOW_DESKTOP,
+    A_SHOW_DESKTOP,
+    A_UNSHOW_DESKTOP,
+    A_TOGGLE_DOCK_AUTOHIDE,
+    A_RECONFIGURE,
+    A_RESTART,
+    A_EXIT,
+    A_DEBUG,
+    A_ACTIVATE,
+    A_FOCUS,
+    A_RAISE,
+    A_LOWER,
+    A_RAISE_LOWER,
+    A_UNFOCUS,
+    A_FOCUS_TO_BOTTOM,
+    A_ICONIFY,
+    A_CLOSE,
+    A_TOGGLE_SHADE,
+    A_SHADE,
+    A_UNSHADE,
+    A_TOGGLE_OMNIPRESENT,
+    A_TOGGLE_MAXIMIZE_FULL,
+    A_MAXIMIZE_FULL,
+    A_UNMAXIMIZE_FULL,
+    A_TOGGLE_MAXIMIZE_VERT,
+    A_MAXIMIZE_VERT,
+    A_UNMAXIMIZE_VERT,
+    A_TOGGLE_MAXIMIZE_HORZ,
+    A_MAXIMIZE_HORZ,
+    A_UNMAXIMIZE_HORZ,
+    A_TOGGLE_FULLSCREEN,
+    A_TOGGLE_DECORATIONS,
+    A_SEND_TO_DESKTOP,
+    A_SEND_TO_DESKTOP_NEXT,
+    A_SEND_TO_DESKTOP_PREVIOUS,
+    A_SEND_TO_DESKTOP_LEFT,
+    A_SEND_TO_DESKTOP_RIGHT,
+    A_SEND_TO_DESKTOP_UP,
+    A_SEND_TO_DESKTOP_DOWN,
+    A_MOVE,
+    A_RESIZE,
+    A_MOVE_TO_CENTER,
+    A_MOVE_RELATIVE,
+    A_RESIZE_RELATIVE,
+    A_MOVE_TO_EDGE_NORTH,
+    A_MOVE_TO_EDGE_SOUTH,
+    A_MOVE_TO_EDGE_WEST,
+    A_MOVE_TO_EDGE_EAST,
+    A_MOVE_FROM_EDGE_NORTH,
+    A_MOVE_FROM_EDGE_SOUTH,
+    A_MOVE_FROM_EDGE_WEST,
+    A_MOVE_FROM_EDGE_EAST,
+    A_GROW_TO_EDGE_NORTH,
+    A_GROW_TO_EDGE_SOUTH,
+    A_GROW_TO_EDGE_WEST,
+    A_GROW_TO_EDGE_EAST,
+    A_SHADE_LOWER,
+    A_UNSHADE_RAISE,
+    A_TOGGLE_ALWAYS_ON_TOP,
+    A_TOGGLE_ALWAYS_ON_BOTTOM,
+    A_SEND_TO_TOP_LAYER,
+    A_SEND_TO_BOTTOM_LAYER,
+    A_SEND_TO_NORMAL_LAYER,
+
+    NUM_ACTIONS
+} Action;
+
+typedef enum {
+    O_EXECUTE,
+    O_MENU,
+    O_DIALOG,
+    O_ALL_DESKTOPS,
+    O_PANELS,
+    O_INCLUDE_DESKTOP,
+    O_LINEAR,
+    O_DESKTOP,
+    O_WRAP,
+    O_STRING,
+    O_FOLLOW,
+    O_EDGE,
+    O_X,
+    O_Y,
+    O_LEFT,
+    O_RIGHT,
+    O_UP,
+    O_DOWN,
+    O_NUM_OPTIONS
+} Option;
+
+
+gchar * action_option_get_text_from_name(Option name);
+Option action_option_get_name_from_text(const gchar *act, const gchar *opt);
+GType action_option_get_type_from_text(const gchar *act, const gchar *opt);
+gchar * action_get_text_from_name(Action name);
+Action action_get_name_from_text(const gchar *text);
+
+#endif
diff --git a/src/keyboard.c b/src/keyboard.c
new file mode 100644 (file)
index 0000000..946fcd6
--- /dev/null
@@ -0,0 +1,1261 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   keyboard.c for ObConf, the configuration tool for Openbox
+   Copyright (c) 2007   Justin Stallard
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "main.h"
+#include "tree.h"
+#include "keyboard.h"
+#include "actions.h"
+
+#include "gettext.h"
+
+#include <openbox/parse.h>
+
+#define SHIFT   "Shift"
+#define CONTROL "Control"
+#define ALT     "Alt"
+#define SUPER   "Super"
+#define META    "Meta"
+#define HYPER   "Hyper"
+
+enum
+{
+    KEY_COL,
+    ACTION_COL,
+    OPTION_COL,
+    OVAL_STR_COL,
+    OVAL_BOOL_COL,
+    OVAL_INT_COL,
+    OVAL_TYPE_STR_COL,
+    OVAL_TYPE_BOOL_COL,
+    OVAL_TYPE_INT_COL,
+    EDITABLE_COL,
+    NUM_COLS
+};
+
+static GtkTreeStore *key_store;
+static GtkListStore *action_store;
+
+static guint chain_press_id;
+static guint chain_release_id;
+static guint key_press_id;
+static guint key_release_id;
+
+
+gboolean on_chain_quit_key_key_release_event(GtkWidget *w,
+                                             GdkEventKey *event,
+                                             gpointer data);
+gboolean on_chain_quit_key_grab_key_press(GtkWidget *w,
+                                          GdkEventKey *event,
+                                          gpointer data);
+gboolean on_chain_quit_key_grab_key_release(GtkWidget *w,
+                                            GdkEventKey *event,
+                                            gpointer data);
+gboolean on_key_bindings_key_press_event(GtkWidget *w,
+                                         GdkEventKey *event,
+                                         gpointer it);
+gboolean on_key_bindings_key_release_event(GtkWidget *w,
+                                           GdkEventKey *event,
+                                           gpointer it);
+void on_tool_edit_clicked(GtkToolButton *w,
+                          gpointer data);
+void on_tool_add_chain_clicked(GtkToolButton *w,
+                               gpointer data);
+void on_tool_new_clicked(GtkToolButton *w,
+                         gpointer data);
+void on_tool_add_action_clicked(GtkToolButton *w,
+                                gpointer data);
+void on_tool_delete_clicked(GtkToolButton *w,
+                            gpointer data);
+void on_chain_quit_key_changed(GtkEntry *w, gpointer data);
+void on_action_edited(GtkCellRendererText *w,
+                      const gchar *path_string,
+                      const gchar *new_text,
+                      gpointer data);
+void on_cell_renderer_toggle_toggled(GtkCellRendererToggle *w,
+                                     const gchar *path_string,
+                                     gpointer data);
+void on_cell_renderer_text_edited(GtkCellRendererText *w,
+                                  const gchar *path_string,
+                                  const gchar *new_text,
+                                  gpointer data);
+void on_cell_renderer_spin_edited(GtkCellRendererText *w,
+                                  const gchar *path_string,
+                                  const gchar *new_text,
+                                  gpointer data);
+
+void keyboard_option_add_new(GtkTreeIter *parent,
+                             const gchar *oname,
+                             GType otype,
+                             gpointer oval);
+void keyboard_key_store_add_default_options(GtkTreeIter *it);
+void keyboard_fill_key_store(GtkTreeIter *parent, xmlNode *a_node);
+void keyboard_write_bindings();
+void keyboard_fill_action_store();
+
+
+
+gboolean on_chain_quit_key_key_release_event(GtkWidget *w,
+                                             GdkEventKey *event,
+                                             gpointer data)
+{
+    GdkKeymapKey key;
+    gchar *key_name;
+
+    key.keycode = event->hardware_keycode;
+    key.group = 0;
+    key.level = 0;
+
+    key_name = gdk_keyval_name(gdk_keymap_lookup_key(NULL, &key));
+
+    /* if key pressed is not "Return", do nothing and let the default handler
+     * take action. */
+    if (strncmp(key_name, "Return", strlen("Return")))
+    {
+        return FALSE;
+    } else {
+        /* "Return" was pressed...prepare for user to set a keybinding */
+
+        /* grab the keyboard */
+        gdk_keyboard_grab(gtk_widget_get_parent_window(w), FALSE, event->time);
+
+        /* disable current handler */
+        g_signal_handler_disconnect(w, chain_release_id);
+
+        /* enable new handler, along with key-release-event handler */
+        chain_press_id = g_signal_connect
+                                (w, "key-press-event",
+                                 G_CALLBACK(on_chain_quit_key_grab_key_press),
+                                 NULL);
+        chain_release_id = g_signal_connect
+                               (w, "key-release-event",
+                                G_CALLBACK(on_chain_quit_key_grab_key_release),
+                                NULL);
+
+        /* default handler does not run */
+        return TRUE;
+    }
+}
+
+gboolean on_chain_quit_key_grab_key_press(GtkWidget *w,
+                                          GdkEventKey *event,
+                                          gpointer data)
+{
+    gchar c[3];
+    GdkKeymapKey key;
+    gchar *key_name;
+    GString *txt;
+
+    key.keycode = event->hardware_keycode;
+    key.group = 0;
+    key.level = 0;
+
+    key_name = gdk_keyval_name(gdk_keymap_lookup_key(NULL, &key));
+
+    c[1] = '-';
+    c[2] = (gchar) NULL;
+
+    txt = g_string_new(gtk_entry_get_text(GTK_ENTRY(w)));
+
+    /* if the last char in the entry is not a '-', then the user has started
+     * entering the new keybinding, so clear the entry first...              */
+    if (txt->str[txt->len - 1] != (gchar) '-')
+    {
+        gtk_entry_set_text(GTK_ENTRY(w), "");
+    }
+
+    if (!strncmp(key_name, SHIFT, strlen(SHIFT)))
+    {
+        c[0] = 'S';
+    }
+    else if (!strncmp(key_name, CONTROL, strlen(CONTROL)))
+    {
+        c[0] = 'C';
+    }
+    else if (!strncmp(key_name, ALT, strlen(ALT)))
+    {
+        c[0] = 'A';
+    }
+    else if (!strncmp(key_name, SUPER, strlen(SUPER)))
+    {
+        c[0] = 'W';
+    }
+    else if (!strncmp(key_name, META, strlen(SUPER)))
+    {
+        c[0] = 'C';
+    }
+    else if (!strncmp(key_name, HYPER, strlen(HYPER)))
+    {
+        c[0] = 'H';
+    }
+    else
+    {
+        c[0] = (gchar) NULL;
+        gtk_entry_append_text(GTK_ENTRY(w), key_name);
+    }
+    gtk_entry_append_text(GTK_ENTRY(w), c);
+
+
+    return TRUE;
+}
+
+gboolean on_chain_quit_key_grab_key_release(GtkWidget *w,
+                                            GdkEventKey *event,
+                                            gpointer data)
+{
+    GdkKeymapKey key;
+    gchar *key_name;
+    gchar *txt;
+    gint len;
+
+    key.keycode = event->hardware_keycode;
+    key.group = 0;
+    key.level = 0;
+
+    key_name = gdk_keyval_name(gdk_keymap_lookup_key(NULL, &key));
+    len = strlen(gtk_entry_get_text(GTK_ENTRY(w)));
+    txt = malloc(sizeof(gchar) * (len + 1));
+    strcpy(txt, gtk_entry_get_text(GTK_ENTRY(w)));
+
+    len--;
+    if (txt[len] == (gchar) '-') {
+        len--;
+        while (len >= 0 && txt[len] != '-')
+        {
+            len--;
+        }
+        txt[len + 1] = '\0';
+
+        gtk_entry_set_text(GTK_ENTRY(w), txt);
+        gtk_entry_append_text(GTK_ENTRY(w), key_name);
+    }
+
+    g_free(txt);
+
+    /* done getting keybinding */
+
+    /* ungrab keyboard */
+    gdk_keyboard_ungrab(event->time);
+
+    /* disable current handler */
+    g_signal_handler_disconnect(w, chain_press_id);
+    g_signal_handler_disconnect(w, chain_release_id);
+
+    /* re-enable old handler */
+    chain_release_id = g_signal_connect
+                            (w, "key-release-event",
+                             G_CALLBACK(on_chain_quit_key_key_release_event),
+                             NULL);
+
+    return TRUE;
+}
+
+gboolean on_key_bindings_key_press_event(GtkWidget *w,
+                                         GdkEventKey *event,
+                                         gpointer it)
+{
+    gchar c[3];
+    GdkKeymapKey key;
+    gchar *key_name;
+    gchar *txt;
+    gchar *new_text;
+    gint len;
+
+    key.keycode = event->hardware_keycode;
+    key.group = 0;
+    key.level = 0;
+
+    key_name = gdk_keyval_name(gdk_keymap_lookup_key(NULL, &key));
+
+    c[1] = '-';
+    c[2] = (gchar) NULL;
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, KEY_COL, &txt, -1);
+    len = strlen(txt);
+
+    /* if the last char in the entry is not a '-', then the user has started
+     * entering the new keybinding, so clear the entry first...              */
+    if (txt[len - 1] != (gchar) '-')
+    {
+        g_free(txt);
+        gtk_tree_store_set(GTK_TREE_MODEL(key_store), (GtkTreeIter *) it,
+                           KEY_COL, "", -1);
+    }
+
+    if (!strncmp(key_name, SHIFT, strlen(SHIFT)))
+    {
+        c[0] = 'S';
+    }
+    else if (!strncmp(key_name, CONTROL, strlen(CONTROL)))
+    {
+        c[0] = 'C';
+    }
+    else if (!strncmp(key_name, ALT, strlen(ALT)))
+    {
+        c[0] = 'A';
+    }
+    else if (!strncmp(key_name, SUPER, strlen(SUPER)))
+    {
+        c[0] = 'W';
+    }
+    else if (!strncmp(key_name, META, strlen(SUPER)))
+    {
+        c[0] = 'C';
+    }
+    else if (!strncmp(key_name, HYPER, strlen(HYPER)))
+    {
+        c[0] = 'H';
+    }
+    else
+    {
+        c[0] = (gchar) NULL;
+        gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, KEY_COL, &txt, -1);
+        new_text = g_strconcat(txt, key_name, NULL);
+        g_free(txt);
+        gtk_tree_store_set(GTK_TREE_MODEL(key_store), (GtkTreeIter *) it,
+                           KEY_COL, new_text, -1);
+    }
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, KEY_COL, &txt, -1);
+    new_text = g_strconcat(txt, c, NULL);
+    g_free(txt);
+    gtk_tree_store_set(GTK_TREE_MODEL(key_store), (GtkTreeIter *) it,
+                       KEY_COL, new_text, -1);
+
+
+    return TRUE;
+}
+
+gboolean on_key_bindings_key_release_event(GtkWidget *w,
+                                           GdkEventKey *event,
+                                           gpointer it)
+{
+    GdkKeymapKey key;
+    gchar *key_name;
+    gchar *txt;
+    gchar *old_text;
+    gchar *new_text;
+    gint len;
+
+    key.keycode = event->hardware_keycode;
+    key.group = 0;
+    key.level = 0;
+
+    key_name = gdk_keyval_name(gdk_keymap_lookup_key(NULL, &key));
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, KEY_COL, &txt, -1);
+    len = strlen(txt);
+    old_text = g_strdup(txt);
+
+    len--;
+    if (old_text[len] == (gchar) '-') {
+        len--;
+        while (len >= 0 && old_text[len] != '-')
+        {
+            len--;
+        }
+        old_text[len + 1] = '\0';
+
+        new_text = g_strconcat(old_text, key_name, NULL);
+        gtk_tree_store_set(GTK_TREE_MODEL(key_store), (GtkTreeIter *) it,
+                           KEY_COL, new_text, -1);
+        g_free(txt);
+    }
+
+    g_free(old_text);
+
+    /* done getting keybinding */
+
+    /* ungrab keyboard */
+    gdk_keyboard_ungrab(event->time);
+
+    g_free(it);
+
+    /* disable current handler */
+    g_signal_handler_disconnect(w, key_press_id);
+    g_signal_handler_disconnect(w, key_release_id);
+
+    keyboard_write_bindings();
+
+    return TRUE;
+}
+
+void on_tool_edit_clicked(GtkToolButton *w,
+                          gpointer data)
+{
+    GtkWidget *entry;
+    GtkWidget *tv;
+    GtkTreeIter *it;
+    gchar *text;
+
+    tv = get_widget("key_bindings");
+    it = g_malloc(sizeof(GtkTreeIter));
+    if (!gtk_tree_selection_get_selected
+            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)), NULL, it))
+    {
+        g_free(it);
+        return;
+    }
+
+    /* make sure selection is a keybinding */
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, ACTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        g_free(it);
+        return;
+    }
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, OPTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        g_free(it);
+        return;
+    }
+
+    gtk_tree_view_scroll_to_cell
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), it),
+         NULL, TRUE, .5, 0);
+
+    gtk_widget_grab_focus(tv);
+
+    /* etc, etc */
+    key_press_id = g_signal_connect
+                               (tv, "key-press-event",
+                                G_CALLBACK(on_key_bindings_key_press_event),
+                                it);
+    key_release_id = g_signal_connect
+                               (tv, "key-release-event",
+                                G_CALLBACK(on_key_bindings_key_release_event),
+                                it);
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, KEY_COL, &text, -1);
+    g_free(text);
+    gtk_tree_store_set(GTK_TREE_MODEL(key_store), (GtkTreeIter *) it,
+                       KEY_COL, "Enter Keybinding", -1);
+
+    gdk_keyboard_grab
+        (gtk_widget_get_parent_window(tv), FALSE, GDK_CURRENT_TIME);
+}
+
+void on_tool_add_chain_clicked(GtkToolButton *w,
+                               gpointer data)
+{
+    GtkWidget *entry;
+    GtkWidget *tv;
+    GtkTreeIter it, parent, new;
+    gchar *text;
+
+    tv = get_widget("key_bindings");
+    if (!gtk_tree_selection_get_selected
+            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)), NULL, &it))
+    {
+        return;
+    }
+
+    /* make sure selection is a keybinding */
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, OPTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        return;
+    }
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, ACTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        return;
+    }
+
+    /* ensure this keybinding doesn't have any actions */
+    parent = it;
+    if (gtk_tree_model_iter_children
+            (GTK_TREE_MODEL(key_store), &it, &parent))
+    {
+        do
+        {
+            gtk_tree_model_get
+                (GTK_TREE_MODEL(key_store), &it, ACTION_COL, &text, -1);
+
+            if (text != NULL)
+            {
+                return;
+            }
+        } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(key_store), &it));
+    }
+
+    /* add the keychain */
+
+    gtk_tree_store_append(key_store, &new, &parent);
+
+    gtk_tree_view_expand_to_path
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new));
+    gtk_tree_view_scroll_to_cell
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new),
+         NULL, FALSE, 0, 0);
+    gtk_tree_selection_select_iter
+        (gtk_tree_view_get_selection(tv), &new);
+
+    on_tool_edit_clicked(NULL, NULL);
+}
+
+void on_tool_new_clicked(GtkToolButton *w,
+                         gpointer data)
+{
+    GtkWidget *entry;
+    GtkWidget *tv;
+    GtkTreeIter it, parent, new;
+    gchar *text;
+
+    tv = get_widget("key_bindings");
+    if (!gtk_tree_selection_get_selected
+            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)), NULL, &it))
+    {
+        return;
+    }
+
+    /* deal with the topmost ancestor of the selection */
+    while (gtk_tree_store_iter_depth(key_store, &it) > 0)
+    {
+        gtk_tree_model_iter_parent(GTK_TREE_MODEL(key_store), &parent, &it);
+        it = parent;
+    }
+
+    /* insert new keybinding */
+    gtk_tree_store_insert_after(key_store, &new, NULL, &it);
+
+    gtk_tree_view_expand_to_path
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new));
+    gtk_tree_view_scroll_to_cell
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new),
+         NULL, FALSE, 0, 0);
+    gtk_tree_selection_select_iter
+        (gtk_tree_view_get_selection(tv), &new);
+
+    on_tool_edit_clicked(NULL, NULL);
+}
+
+void on_tool_add_action_clicked(GtkToolButton *w,
+                                gpointer data)
+{
+    GtkWidget *entry;
+    GtkWidget *tv;
+    GtkTreeIter it, parent, new;
+    gchar *text;
+
+    tv = get_widget("key_bindings");
+    if (!gtk_tree_selection_get_selected
+            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)), NULL, &it))
+    {
+        return;
+    }
+
+    /* make sure selection is a keybinding or action */
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, OPTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        gtk_tree_model_iter_parent(GTK_TREE_MODEL(key_store), &parent, &it);
+        it = parent;
+    }
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, ACTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        gtk_tree_store_insert_after(key_store, &new, NULL, &it);
+
+        text = action_get_text_from_name(0);
+        gtk_tree_store_set(key_store, &new,
+                                            ACTION_COL, text,
+                                            EDITABLE_COL, TRUE,
+                                            -1);
+        g_free(text);
+        keyboard_key_store_add_default_options(&new);
+    }
+    else
+    {
+        /* The selection must be a keybinding */
+        parent = it;
+        if (gtk_tree_model_iter_children
+                (GTK_TREE_MODEL(key_store), &it, &parent))
+        {
+            /* keychains can't have actions, just keys */
+            do
+            {
+                gtk_tree_model_get
+                    (GTK_TREE_MODEL(key_store), &it, KEY_COL, &text, -1);
+
+                if (text != NULL)
+                {
+                    return;
+                }
+            } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(key_store), &it));
+        }
+
+        gtk_tree_store_insert_after(key_store, &new, &parent, NULL);
+
+        text = action_get_text_from_name(0);
+        gtk_tree_store_set(key_store, &new,
+                                            ACTION_COL, text,
+                                            EDITABLE_COL, TRUE,
+                                            -1);
+        g_free(text);
+        keyboard_key_store_add_default_options(&new);
+    }
+
+    gtk_tree_view_expand_to_path
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new));
+    gtk_tree_view_scroll_to_cell
+        (tv, gtk_tree_model_get_path(GTK_TREE_MODEL(key_store), &new),
+         NULL, FALSE, 0, 0);
+    gtk_tree_selection_select_iter
+        (gtk_tree_view_get_selection(tv), &new);
+
+    keyboard_write_bindings();
+}
+
+void on_tool_delete_clicked(GtkToolButton *w,
+                            gpointer data)
+{
+    GtkWidget *entry;
+    GtkWidget *tv;
+    GtkTreeIter it;
+    gchar *text;
+
+    tv = get_widget("key_bindings");
+    if (!gtk_tree_selection_get_selected
+            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)), NULL, &it))
+    {
+        return;
+    }
+
+    /* make sure selection is a keybinding or action */
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, OPTION_COL, &text, -1);
+
+    if (text != NULL)
+    {
+        return;
+    }
+
+    gtk_tree_store_remove(key_store, &it);
+
+    keyboard_write_bindings();
+}
+
+void on_chain_quit_key_changed(GtkEntry *w, gpointer data)
+{
+    gchar *chain;
+
+    chain = g_strdup(gtk_entry_get_text(w));
+    tree_set_string("keyboard/chainQuitKey", chain);
+
+    g_free(chain);
+}
+
+void on_action_edited(GtkCellRendererText *w,
+                      const gchar *path_string,
+                      const gchar *new_text,
+                      gpointer data)
+{
+    GtkTreePath *path;
+    GtkTreeIter it, child;
+    gchar *old_text;
+    GtkWidget *tv;
+
+    path = gtk_tree_path_new_from_string(path_string);
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(key_store), &it, path);
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it,
+                       ACTION_COL, &old_text, -1);
+
+    if (!strcmp(new_text, old_text))
+    {
+        return;
+    }
+
+    g_free(old_text);
+
+    gtk_tree_store_set(key_store, &it, ACTION_COL, g_strdup(new_text), -1);
+
+    /* remove old options */
+    if (gtk_tree_model_iter_children(GTK_TREE_MODEL(key_store), &child, &it))
+    {
+        while (gtk_tree_store_remove(key_store, &child));
+    }
+
+    /* add default options */
+    keyboard_key_store_add_default_options(&it);
+
+    /* expand so options are visible*/
+    tv = get_widget("key_bindings");
+    gtk_tree_view_expand_row(GTK_TREE_VIEW(tv), path, FALSE);
+
+    keyboard_write_bindings();
+}
+
+void on_cell_renderer_toggle_toggled(GtkCellRendererToggle *w,
+                                     const gchar *path_string,
+                                     gpointer data)
+{
+    GtkTreePath *path;
+    GtkTreeIter iter;
+    gboolean oldval;
+    gboolean tmp;
+
+    path = gtk_tree_path_new_from_string(path_string);
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(key_store), &iter, path);
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &iter,
+                       OVAL_BOOL_COL, &oldval,
+                       -1);
+
+    gtk_tree_store_set(key_store, &iter, OVAL_BOOL_COL, !oldval, -1);
+
+    keyboard_write_bindings();
+}
+
+void on_cell_renderer_text_edited(GtkCellRendererText *w,
+                                  const gchar *path_string,
+                                  const gchar *new_text,
+                                  gpointer data)
+{
+    GtkTreePath *path;
+    GtkTreeIter it;
+    gchar *old_text;
+
+    path = gtk_tree_path_new_from_string(path_string);
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(key_store), &it, path);
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, 0, &old_text, -1);
+    g_free(old_text);
+
+    gtk_tree_store_set(key_store, &it, OVAL_STR_COL, g_strdup(new_text), -1);
+
+    keyboard_write_bindings();
+}
+
+void on_cell_renderer_spin_edited(GtkCellRendererText *w,
+                                  const gchar *path_string,
+                                  const gchar *new_text,
+                                  gpointer data)
+{
+    GtkTreePath *path;
+    GtkTreeIter it;
+    gchar *old_text;
+    const gchar *c;
+
+    for (c = new_text; *c != '\0'; c++)
+    {
+        if (!g_ascii_isdigit(*c))
+            return;
+    }
+
+    path = gtk_tree_path_new_from_string(path_string);
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(key_store), &it, path);
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), &it, 0, &old_text, -1);
+    g_free(old_text);
+
+    gtk_tree_store_set(key_store, &it, OVAL_STR_COL, g_strdup(new_text), -1);
+
+    keyboard_write_bindings();
+}
+
+
+void keyboard_option_add_new(GtkTreeIter *parent,
+                             const gchar *oname,
+                             GType otype,
+                             gpointer oval)
+{
+    GtkTreeIter new;
+    GtkAdjustment *adj;
+    gchar buf[16];
+
+    gtk_tree_store_append(key_store, &new, parent);
+
+    switch (otype)
+    {
+        case G_TYPE_STRING:
+            gtk_tree_store_set(key_store, &new,
+                            OPTION_COL, oname,
+                            OVAL_STR_COL, (gchar *) oval,
+                            OVAL_TYPE_STR_COL, TRUE,
+                            EDITABLE_COL, FALSE,
+                            -1);
+            break;
+        case G_TYPE_BOOLEAN:
+            gtk_tree_store_set(key_store, &new,
+                            OPTION_COL, oname,
+                            OVAL_BOOL_COL, (gboolean) oval,
+                            OVAL_TYPE_BOOL_COL, TRUE,
+                            EDITABLE_COL, FALSE,
+                            -1);
+            break;
+        case G_TYPE_INT:
+            adj = GTK_ADJUSTMENT(gtk_adjustment_new
+                                        ((gint) oval,
+                                         -5000, 5000, 1, 1, 0));
+            g_ascii_dtostr(buf, 16, (gdouble)((gint) oval));
+            gtk_tree_store_set(key_store, &new,
+                            OPTION_COL, oname,
+                            OVAL_INT_COL, G_OBJECT(adj),
+                            OVAL_STR_COL, buf,
+                            OVAL_TYPE_INT_COL, TRUE,
+                            EDITABLE_COL, FALSE,
+                            -1);
+            break;
+    }
+
+}
+
+void keyboard_key_store_add_default_options(GtkTreeIter *it)
+{
+    gchar *atext;
+    Action action;
+
+    gtk_tree_model_get(GTK_TREE_MODEL(key_store), it, ACTION_COL, &atext, -1);
+
+    action = action_get_name_from_text(atext);
+
+    switch (action)
+    {
+        case A_EXECUTE:
+            keyboard_option_add_new(it, "execute", G_TYPE_STRING, "");
+            break;
+        case A_SHOW_MENU:
+            keyboard_option_add_new(it, "menu", G_TYPE_STRING, "");
+            break;
+        case A_NEXT_WINDOW:
+        case A_PREVIOUS_WINDOW:
+            keyboard_option_add_new(it, "dialog", G_TYPE_BOOLEAN,
+                                                            (gpointer) TRUE);
+            keyboard_option_add_new(it, "allDesktops", G_TYPE_BOOLEAN, FALSE);
+            keyboard_option_add_new(it, "panels", G_TYPE_BOOLEAN, FALSE);
+            keyboard_option_add_new(it, "desktop", G_TYPE_BOOLEAN, FALSE);
+            keyboard_option_add_new(it, "linear", G_TYPE_BOOLEAN, FALSE);
+            break;
+        case A_DIRECTIONAL_FOCUS_NORTH:
+        case A_DIRECTIONAL_FOCUS_SOUTH:
+        case A_DIRECTIONAL_FOCUS_EAST:
+        case A_DIRECTIONAL_FOCUS_WEST:
+        case A_DIRECTIONAL_FOCUS_NORTH_WEST:
+        case A_DIRECTIONAL_FOCUS_NORTH_EAST:
+        case A_DIRECTIONAL_FOCUS_SOUTH_WEST:
+        case A_DIRECTIONAL_FOCUS_SOUTH_EAST:
+            keyboard_option_add_new(it, "dialog", G_TYPE_BOOLEAN,
+                                                            (gpointer) TRUE);
+            break;
+        case A_DESKTOP:
+            keyboard_option_add_new(it, "desktop", G_TYPE_INT, 1);
+            break;
+        case A_DESKTOP_NEXT:
+        case A_DESKTOP_PREVIOUS:
+        case A_DESKTOP_LEFT:
+        case A_DESKTOP_RIGHT:
+        case A_DESKTOP_UP:
+        case A_DESKTOP_DOWN:
+            keyboard_option_add_new(it, "dialog", G_TYPE_BOOLEAN, TRUE);
+            keyboard_option_add_new(it, "wrap", G_TYPE_BOOLEAN, TRUE);
+            break;
+        case A_DESKTOP_LAST:
+        case A_ADD_DESKTOP_LAST:
+        case A_REMOVE_DESKTOP_LAST:
+        case A_ADD_DESKTOP_CURRENT:
+        case A_REMOVE_DESKTOP_CURRENT:
+        case A_TOGGLE_SHOW_DESKTOP:
+        case A_SHOW_DESKTOP:
+        case A_UNSHOW_DESKTOP:
+        case A_TOGGLE_DOCK_AUTOHIDE:
+        case A_RECONFIGURE:
+        case A_RESTART:
+        case A_EXIT:
+            break;
+        case A_DEBUG:
+            keyboard_option_add_new(it, "string", G_TYPE_STRING, "");
+            break;
+
+        case A_ACTIVATE:
+        case A_FOCUS:
+        case A_RAISE:
+        case A_LOWER:
+        case A_RAISE_LOWER:
+        case A_UNFOCUS:
+        case A_FOCUS_TO_BOTTOM:
+        case A_ICONIFY:
+        case A_CLOSE:
+        case A_TOGGLE_SHADE:
+        case A_SHADE:
+        case A_UNSHADE:
+        case A_TOGGLE_OMNIPRESENT:
+        case A_TOGGLE_MAXIMIZE_FULL:
+        case A_MAXIMIZE_FULL:
+        case A_UNMAXIMIZE_FULL:
+        case A_TOGGLE_MAXIMIZE_VERT:
+        case A_MAXIMIZE_VERT:
+        case A_UNMAXIMIZE_VERT:
+        case A_TOGGLE_MAXIMIZE_HORZ:
+        case A_MAXIMIZE_HORZ:
+        case A_UNMAXIMIZE_HORZ:
+        case A_TOGGLE_FULLSCREEN:
+        case A_TOGGLE_DECORATIONS:
+            break;
+        case A_SEND_TO_DESKTOP:
+            keyboard_option_add_new(it, "desktop", G_TYPE_INT, 1);
+            keyboard_option_add_new(it, "follow", G_TYPE_BOOLEAN, TRUE);
+            break;
+        case A_SEND_TO_DESKTOP_NEXT:
+        case A_SEND_TO_DESKTOP_PREVIOUS:
+        case A_SEND_TO_DESKTOP_LEFT:
+        case A_SEND_TO_DESKTOP_RIGHT:
+        case A_SEND_TO_DESKTOP_UP:
+        case A_SEND_TO_DESKTOP_DOWN:
+            keyboard_option_add_new(it, "follow", G_TYPE_BOOLEAN, TRUE);
+            keyboard_option_add_new(it, "wrap", G_TYPE_BOOLEAN, TRUE);
+            break;
+        case A_MOVE:
+            break;
+        case A_RESIZE:
+            keyboard_option_add_new(it, "edge", G_TYPE_STRING, "");
+            break;
+        case A_MOVE_TO_CENTER:
+            break;
+        case A_MOVE_RELATIVE:
+            keyboard_option_add_new(it, "x", G_TYPE_INT, 0);
+            keyboard_option_add_new(it, "y", G_TYPE_INT, 0);
+            break;
+        case A_RESIZE_RELATIVE:
+            keyboard_option_add_new(it, "left", G_TYPE_INT, 0);
+            keyboard_option_add_new(it, "right", G_TYPE_INT, 0);
+            keyboard_option_add_new(it, "up", G_TYPE_INT, 0);
+            keyboard_option_add_new(it, "down", G_TYPE_INT, 0);
+            break;
+        case A_MOVE_TO_EDGE_NORTH:
+        case A_MOVE_TO_EDGE_SOUTH:
+        case A_MOVE_TO_EDGE_WEST:
+        case A_MOVE_TO_EDGE_EAST:
+        case A_MOVE_FROM_EDGE_NORTH:
+        case A_MOVE_FROM_EDGE_SOUTH:
+        case A_MOVE_FROM_EDGE_WEST:
+        case A_MOVE_FROM_EDGE_EAST:
+        case A_GROW_TO_EDGE_NORTH:
+        case A_GROW_TO_EDGE_SOUTH:
+        case A_GROW_TO_EDGE_WEST:
+        case A_GROW_TO_EDGE_EAST:
+        case A_SHADE_LOWER:
+        case A_UNSHADE_RAISE:
+        case A_TOGGLE_ALWAYS_ON_TOP:
+        case A_TOGGLE_ALWAYS_ON_BOTTOM:
+        case A_SEND_TO_TOP_LAYER:
+        case A_SEND_TO_BOTTOM_LAYER:
+        case A_SEND_TO_NORMAL_LAYER:
+            break;
+
+        case NUM_ACTIONS:
+        default:
+            break;
+    }
+
+    return;
+}
+
+void keyboard_fill_key_store(GtkTreeIter *parent, xmlNode *a_node)
+{
+    xmlNode *cur_node = NULL;
+    GtkTreeIter iter;
+    gchar *attr;
+    gchar *option;
+    GType otype;
+    gboolean tmpbool;
+    GtkAdjustment *adj;
+
+
+    for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
+        if (!strcmp("keybind", cur_node->name)) {
+            gtk_tree_store_append(key_store, &iter, parent);
+            parse_attr_string("key", cur_node, &attr);
+
+            gtk_tree_store_set(key_store, &iter, KEY_COL, attr, -1);
+        } else if (!strcmp("action", cur_node->name)) {
+            gtk_tree_store_append(key_store, &iter, parent);
+            parse_attr_string("name", cur_node, &attr);
+
+            gtk_tree_store_set(key_store, &iter,
+                                                ACTION_COL, attr,
+                                                EDITABLE_COL, TRUE,
+                                                -1);
+
+            keyboard_key_store_add_default_options(&iter);
+        } else if (!strcmp("action", cur_node->parent->name)) {
+            gtk_tree_model_iter_children(GTK_TREE_MODEL(key_store),
+                                         &iter,
+                                         parent);
+            parse_attr_string("name", cur_node->parent, &attr);
+
+            do
+            {
+                gtk_tree_model_get(GTK_TREE_MODEL(key_store), &iter,
+                                   OPTION_COL, &option, -1);
+                if (!strcmp(option, cur_node->name))
+                {
+                    otype = action_option_get_type_from_text
+                                (attr, cur_node->name);
+                    switch (otype)
+                    {
+                        case G_TYPE_STRING:
+                            gtk_tree_store_set(key_store, &iter,
+                                    OVAL_STR_COL, parse_string(doc, cur_node),
+                                    -1);
+                            break;
+                        case G_TYPE_BOOLEAN:
+                            tmpbool = !strcmp
+                                        (parse_string(doc, cur_node), "yes")
+                                      ?TRUE:FALSE;
+                            gtk_tree_store_set(key_store, &iter,
+                                            OVAL_BOOL_COL, tmpbool,
+                                            -1);
+                            break;
+                        case G_TYPE_INT:
+                            adj = GTK_ADJUSTMENT(gtk_adjustment_new
+                                                    (parse_int(doc, cur_node),
+                                                     -5000, 5000, 1, 1, 0));
+                            gtk_tree_store_set(key_store, &iter,
+                                    OVAL_INT_COL, G_OBJECT(adj),
+                                    OVAL_STR_COL, parse_string(doc, cur_node),
+                                    -1);
+                            break;
+                    }
+
+                    break;
+                }
+            } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(key_store), &iter));
+        }
+
+        keyboard_fill_key_store(&iter, cur_node->children);
+    }
+}
+
+void keyboard_write_bindings_tree(GtkTreeIter *it_parent, xmlNodePtr xml_parent)
+{
+    GtkTreeIter iter;
+    xmlNodePtr new_node;
+    gchar *key;
+    gchar *action;
+    gchar *option;
+    GType optype;
+    gchar *ovalstr;
+    gboolean ovalbool;
+
+    if (!gtk_tree_model_iter_children
+            (GTK_TREE_MODEL(key_store), &iter, it_parent))
+    {
+       return;
+    }
+
+    do
+    {
+       gtk_tree_model_get(GTK_TREE_MODEL(key_store), &iter,
+                          KEY_COL, &key,
+                          ACTION_COL, &action,
+                          OPTION_COL, &option, -1);
+       if (key != NULL)
+       {
+           new_node = xmlNewChild(xml_parent, NULL, "keybind", NULL);
+           xmlSetProp(new_node, "key", key);
+       }
+       else if (action != NULL)
+       {
+           new_node = xmlNewChild(xml_parent, NULL, "action", NULL);
+           xmlSetProp(new_node, "name", action);
+       }
+       else if (option != NULL)
+       {
+           g_free(action);
+           gtk_tree_model_get(GTK_TREE_MODEL(key_store), it_parent,
+                              ACTION_COL, &action, -1);
+           optype = action_option_get_type_from_text(action, option);
+
+           switch (optype)
+           {
+               case G_TYPE_STRING:
+               case G_TYPE_INT:
+                   gtk_tree_model_get(GTK_TREE_MODEL(key_store), &iter,
+                                      OVAL_STR_COL, &ovalstr, -1);
+                   new_node =
+                       xmlNewTextChild(xml_parent, NULL, option, ovalstr);
+                   g_free(ovalstr);
+                   break;
+               case G_TYPE_BOOLEAN:
+                   gtk_tree_model_get(GTK_TREE_MODEL(key_store), &iter,
+                                      OVAL_BOOL_COL, &ovalbool, -1);
+                   new_node =
+                       xmlNewTextChild
+                           (xml_parent, NULL, option, ovalbool?"yes":"no");
+                   break;
+               default:
+                   break;
+           }
+       }
+
+       g_free(key);
+       g_free(action);
+       g_free(option);
+
+       keyboard_write_bindings_tree(&iter, new_node);
+    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(key_store), &iter));
+}
+
+void keyboard_write_bindings()
+{
+    xmlNodePtr kb, c;
+
+    kb = tree_get_node("keyboard", NULL);
+    while (c = kb->children)
+    {
+       xmlUnlinkNode(c);
+       xmlFreeNode(c);
+    }
+
+    on_chain_quit_key_changed(GTK_ENTRY(get_widget("chain_quit_key")), NULL);
+
+    keyboard_write_bindings_tree(NULL, kb);
+
+    tree_apply();
+}
+
+void keyboard_fill_action_store()
+{
+    GtkTreeIter it;
+    gint i;
+    gchar *action;
+    action_store = gtk_list_store_new(1, G_TYPE_STRING);
+
+    for (i = 0; i < NUM_ACTIONS; i++)
+    {
+        action = action_get_text_from_name(i);
+
+        gtk_list_store_append(action_store, &it);
+        gtk_list_store_set(action_store, &it, 0, action, -1);
+
+        g_free(action);
+    }
+}
+
+
+void keyboard_setup_tab()
+{
+    GtkCellRenderer *render;
+    GtkTreeViewColumn *column;
+    GtkWidget *w;
+    GtkTreeIter tree_iter;
+    gchar *chainquitkey;
+    xmlNode *keyboard_node = tree_get_node("keyboard", NULL)->children;
+
+    /* signal handlers */
+
+    w = get_widget("chain_quit_key");
+    chainquitkey = tree_get_string("keyboard/chainQuitKey", "C-g");
+    gtk_entry_set_text(GTK_ENTRY(w), chainquitkey);
+    g_free(chainquitkey);
+    chain_release_id = g_signal_connect
+                            (w, "key-release-event",
+                             G_CALLBACK(on_chain_quit_key_key_release_event),
+                             NULL);
+
+    /* treeview setup */
+    w = get_widget("key_bindings");
+
+    key_store = gtk_tree_store_new(NUM_COLS,
+                                   G_TYPE_STRING,
+                                   G_TYPE_STRING,
+                                   G_TYPE_STRING,
+                                   G_TYPE_STRING,
+                                   G_TYPE_BOOLEAN,
+                                   G_TYPE_OBJECT, /* a GtkAdjustment */
+                                   G_TYPE_BOOLEAN,
+                                   G_TYPE_BOOLEAN,
+                                   G_TYPE_BOOLEAN,
+                                   G_TYPE_BOOLEAN);
+    gtk_tree_view_set_model(GTK_TREE_VIEW(w), GTK_TREE_MODEL(key_store));
+
+    g_object_unref(key_store);
+    gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(w)),
+                                GTK_SELECTION_SINGLE);
+
+    /* load data */
+    keyboard_fill_key_store(NULL, keyboard_node);
+    keyboard_fill_action_store();
+
+    /* key column */
+    render = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes
+        ("Key", render, "text", KEY_COL, NULL);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
+
+    /* action column */
+    render = gtk_cell_renderer_combo_new();
+    g_signal_connect(render, "edited",
+                     G_CALLBACK(on_action_edited), NULL);
+    g_object_set(render, "has-entry", FALSE,
+                         "model", action_store,
+                         "text-column", 0,
+                         NULL);
+    column = gtk_tree_view_column_new_with_attributes
+                 ("Action", render, "text", ACTION_COL,
+                                    "editable", EDITABLE_COL,
+                                    NULL);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
+
+    /* option column */
+    render = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes
+        ("Option", render, "text", OPTION_COL, NULL);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
+
+    /* option value column */
+    render = gtk_cell_renderer_text_new();
+    g_signal_connect(render, "edited",
+                     G_CALLBACK(on_cell_renderer_text_edited), NULL);
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_column_set_title(column, "Option Value");
+    gtk_tree_view_column_pack_start(column, render, TRUE);
+    gtk_tree_view_column_set_attributes
+        (column, render, "text", OVAL_STR_COL,
+                                 "editable", OVAL_TYPE_STR_COL,
+                                 "visible", OVAL_TYPE_STR_COL, NULL);
+
+    render = gtk_cell_renderer_toggle_new();
+    g_signal_connect(render, "toggled",
+                     G_CALLBACK(on_cell_renderer_toggle_toggled), NULL);
+    gtk_tree_view_column_pack_start(column, render, TRUE);
+    gtk_tree_view_column_set_attributes
+        (column, render, "active", OVAL_BOOL_COL,
+                         "visible", OVAL_TYPE_BOOL_COL, NULL);
+
+    render = gtk_cell_renderer_spin_new();
+    g_signal_connect(render, "edited",
+                     G_CALLBACK(on_cell_renderer_spin_edited), NULL);
+    gtk_tree_view_column_pack_start(column, render, TRUE);
+    gtk_tree_view_column_set_attributes
+        (column, render, "adjustment", OVAL_INT_COL,
+                         "text", OVAL_STR_COL,
+                         "editable", OVAL_TYPE_INT_COL,
+                         "visible", OVAL_TYPE_INT_COL, NULL);
+
+    gtk_tree_view_append_column(GTK_TREE_VIEW(w), column);
+}
diff --git a/src/keyboard.h b/src/keyboard.h
new file mode 100644 (file)
index 0000000..4377ff0
--- /dev/null
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   keyboard.h for ObConf, the configuration tool for Openbox
+   Copyright (c) 2007        Justin Stallard
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef obconf__keyboard_h
+#define obconf__keyboard_h
+
+#include <gtk/gtk.h>
+
+void keyboard_setup_tab();
+
+#endif