[gtk+/wip/matthiasc/help-overlay: 1/3] Add GtkShortcutsWindow



commit a900b445e71d1aff073b6167c2930f361fbd4b74
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Oct 11 16:39:18 2015 -0400

    Add GtkShortcutsWindow
    
    This is a toplevel window that is tailored towards showing
    help for shortcuts in an application.
    
    This implementation is shamelessly stolen from gnome-builder,
    thanks to Christian Hergert.

 gtk/Makefile.am                   |   16 +
 gtk/gtkshortcutlabel.c            |  218 +++++++++
 gtk/gtkshortcutlabelprivate.h     |   47 ++
 gtk/gtkshortcutscolumn.c          |   47 ++
 gtk/gtkshortcutscolumnprivate.h   |   42 ++
 gtk/gtkshortcutsgesture.c         |  211 ++++++++
 gtk/gtkshortcutsgestureprivate.h  |   42 ++
 gtk/gtkshortcutsgroup.c           |  123 +++++
 gtk/gtkshortcutsgroupprivate.h    |   41 ++
 gtk/gtkshortcutspage.c            |   47 ++
 gtk/gtkshortcutspageprivate.h     |   42 ++
 gtk/gtkshortcutsshortcut.c        |  174 +++++++
 gtk/gtkshortcutsshortcutprivate.h |   42 ++
 gtk/gtkshortcutsview.c            |  231 +++++++++
 gtk/gtkshortcutsviewprivate.h     |   45 ++
 gtk/gtkshortcutswindow.c          |  970 +++++++++++++++++++++++++++++++++++++
 gtk/gtkshortcutswindow.h          |   55 +++
 17 files changed, 2393 insertions(+), 0 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 88e30f8..7a0adcc 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -269,6 +269,7 @@ gtk_public_h_sources =              \
        gtkseparatormenuitem.h  \
        gtkseparatortoolitem.h  \
        gtksettings.h           \
+       gtkshortcutswindow.h    \
        gtkshow.h               \
        gtkstacksidebar.h       \
        gtksizegroup.h          \
@@ -505,6 +506,13 @@ gtk_private_h_sources =            \
        gtkselectionprivate.h   \
        gtksidebarrowprivate.h  \
        gtksettingsprivate.h    \
+       gtkshortcutscolumnprivate.h     \
+       gtkshortcutsgestureprivate.h    \
+       gtkshortcutsgrouprivate.h       \
+       gtkshortcutlabelprivate.h       \
+       gtkshortcutsshortcutprivate.h   \
+       gtkshortcutspageprivate.h       \
+       gtkshortcutsviewprivate.h       \
        gtksizegroup-private.h  \
        gtksizerequestcacheprivate.h    \
        gtksocketprivate.h      \
@@ -805,6 +813,14 @@ gtk_base_c_sources =               \
        gtkseparatormenuitem.c  \
        gtkseparatortoolitem.c  \
        gtksettings.c           \
+       gtkshortcutscolumn.c    \
+       gtkshortcutsgesture.c   \
+       gtkshortcutsgroup.c     \
+       gtkshortcutlabel.c      \
+       gtkshortcutspage.c      \
+       gtkshortcutsshortcut.c  \
+       gtkshortcutsview.c      \
+       gtkshortcutswindow.c    \
        gtksidebarrow.c         \
        gtksizegroup.c          \
        gtksizerequest.c        \
diff --git a/gtk/gtkshortcutlabel.c b/gtk/gtkshortcutlabel.c
new file mode 100644
index 0000000..bf31957
--- /dev/null
+++ b/gtk/gtkshortcutlabel.c
@@ -0,0 +1,218 @@
+/* gtkshortcutlabel.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutlabelprivate.h"
+#include "gtklabel.h"
+#include "gtkframe.h"
+#include "gtkstylecontext.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+struct _GtkShortcutLabel
+{
+  GtkBox  parent_instance;
+  gchar  *accelerator;
+};
+
+struct _GtkShortcutLabelClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutLabel, gtk_shortcut_label, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_ACCELERATOR,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+gtk_shortcut_label_rebuild (GtkShortcutLabel *self)
+{
+  gchar **keys = NULL;
+  gchar *label = NULL;
+  GdkModifierType modifier = 0;
+  guint key = 0;
+  guint i;
+
+  gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback)gtk_widget_destroy, NULL);
+
+  if (self->accelerator == NULL)
+    return;
+
+  gtk_accelerator_parse (self->accelerator, &key, &modifier);
+  if ((key == 0) && (modifier == 0))
+    return;
+
+  label = gtk_accelerator_get_label (key, modifier);
+  if (label == NULL)
+    return;
+
+  keys = g_strsplit (label, "+", 0);
+  for (i = 0; keys[i]; i++)
+    {
+      GtkFrame *frame;
+      GtkLabel *disp;
+
+      if (i > 0)
+        {
+          GtkLabel *plus;
+          GtkStyleContext *context;
+
+          plus = g_object_new (GTK_TYPE_LABEL,
+                               "label", "+",
+                               "visible", TRUE,
+                               NULL);
+          context = gtk_widget_get_style_context (GTK_WIDGET (plus));
+          gtk_style_context_add_class (context, "dim-label");
+          gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (plus));
+        }
+
+      frame = g_object_new (GTK_TYPE_FRAME,
+                            "visible", TRUE,
+                            NULL);
+      gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (frame));
+
+      /*
+       * FIXME: Check if the item is a modifier.
+       *
+       * If we have a size group, size everything the same except for the
+       * last item. This has the side effect of basically matching all
+       * modifiers together. Not always the case, but simple and easy
+       * hack.
+       */
+      if (keys[i + 1] != NULL)
+        gtk_widget_set_size_request (GTK_WIDGET (frame), 50, -1);
+
+      disp = g_object_new (GTK_TYPE_LABEL,
+                           "label", keys [i],
+                           "visible", TRUE,
+                           NULL);
+      gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (disp));
+    }
+
+  g_strfreev (keys);
+  g_free (label);
+}
+
+static void
+gtk_shortcut_label_finalize (GObject *object)
+{
+  GtkShortcutLabel *self = (GtkShortcutLabel *)object;
+
+  g_free (self->accelerator);
+
+  G_OBJECT_CLASS (gtk_shortcut_label_parent_class)->finalize (object);
+}
+
+static void
+gtk_shortcut_label_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GtkShortcutLabel *self = GTK_SHORTCUT_LABEL (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACCELERATOR:
+      g_value_set_string (value, gtk_shortcut_label_get_accelerator (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcut_label_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GtkShortcutLabel *self = GTK_SHORTCUT_LABEL (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACCELERATOR:
+      gtk_shortcut_label_set_accelerator (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcut_label_class_init (GtkShortcutLabelClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_shortcut_label_finalize;
+  object_class->get_property = gtk_shortcut_label_get_property;
+  object_class->set_property = gtk_shortcut_label_set_property;
+
+  properties[PROP_ACCELERATOR] =
+    g_param_spec_string ("accelerator", P_("Accelerator"), P_("Accelerator"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+gtk_shortcut_label_init (GtkShortcutLabel *self)
+{
+  gtk_box_set_spacing (GTK_BOX (self), 6);
+}
+
+GtkWidget *
+gtk_shortcut_label_new (const gchar *accelerator)
+{
+  return g_object_new (GTK_TYPE_SHORTCUT_LABEL,
+                       "accelerator", accelerator,
+                       NULL);
+}
+
+const gchar *
+gtk_shortcut_label_get_accelerator (GtkShortcutLabel *self)
+{
+  g_return_val_if_fail (GTK_IS_SHORTCUT_LABEL (self), NULL);
+
+  return self->accelerator;
+}
+
+void
+gtk_shortcut_label_set_accelerator (GtkShortcutLabel *self,
+                                    const gchar      *accelerator)
+{
+  g_return_if_fail (GTK_IS_SHORTCUT_LABEL (self));
+
+  if (g_strcmp0 (accelerator, self->accelerator) != 0)
+    {
+      g_free (self->accelerator);
+      self->accelerator = g_strdup (accelerator);
+      gtk_shortcut_label_rebuild (self);
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCELERATOR]);
+    }
+}
diff --git a/gtk/gtkshortcutlabelprivate.h b/gtk/gtkshortcutlabelprivate.h
new file mode 100644
index 0000000..4f5096c
--- /dev/null
+++ b/gtk/gtkshortcutlabelprivate.h
@@ -0,0 +1,47 @@
+/* gtkshortcutlabelprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUT_LABEL_H__
+#define __GTK_SHORTCUT_LABEL_H__
+
+#include <gtk/gtkbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUT_LABEL (gtk_shortcut_label_get_type())
+#define GTK_SHORTCUT_LABEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUT_LABEL, 
GtkShortcutLabel))
+#define GTK_SHORTCUT_LABEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUT_LABEL, 
GtkShortcutLabelClass))
+#define GTK_IS_SHORTCUT_LABEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUT_LABEL))
+#define GTK_IS_SHORTCUT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUT_LABEL))
+#define GTK_SHORTCUT_LABEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUT_LABEL, 
GtkShortcutLabelClass))
+
+
+typedef struct _GtkShortcutLabel      GtkShortcutLabel;
+typedef struct _GtkShortcutLabelClass GtkShortcutLabelClass;
+
+
+GType        gtk_shortcut_label_get_type        (void) G_GNUC_CONST;
+
+GtkWidget   *gtk_shortcut_label_new             (const gchar      *accelerator);
+const gchar *gtk_shortcut_label_get_accelerator (GtkShortcutLabel *self);
+void         gtk_shortcut_label_set_accelerator (GtkShortcutLabel *self,
+                                                 const gchar      *accelerator);
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUT_LABEL_H__ */
diff --git a/gtk/gtkshortcutscolumn.c b/gtk/gtkshortcutscolumn.c
new file mode 100644
index 0000000..88f81b8
--- /dev/null
+++ b/gtk/gtkshortcutscolumn.c
@@ -0,0 +1,47 @@
+/* gtkshortcutscolumn.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutscolumnprivate.h"
+#include "gtkorientable.h"
+
+
+struct _GtkShortcutsColumn
+{
+  GtkBox parent_instance;
+};
+
+struct _GtkShortcutsColumnClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsColumn, gtk_shortcuts_column, GTK_TYPE_BOX)
+
+static void
+gtk_shortcuts_column_class_init (GtkShortcutsColumnClass *klass)
+{
+}
+
+static void
+gtk_shortcuts_column_init (GtkShortcutsColumn *self)
+{
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+  gtk_box_set_spacing (GTK_BOX (self), 22);
+}
diff --git a/gtk/gtkshortcutscolumnprivate.h b/gtk/gtkshortcutscolumnprivate.h
new file mode 100644
index 0000000..0ded67d
--- /dev/null
+++ b/gtk/gtkshortcutscolumnprivate.h
@@ -0,0 +1,42 @@
+/* gtkshortcutscolumnprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_COLUMN_H__
+#define __GTK_SHORTCUTS_COLUMN_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_COLUMN (gtk_shortcuts_column_get_type ())
+#define GTK_SHORTCUTS_COLUMN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUTS_COLUMN, 
GtkShortcutsColumn))
+#define GTK_SHORTCUTS_COLUMN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_COLUMN, 
GtkShortcutsColumnClass))
+#define GTK_IS_COLUMNS_COLUMN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUTS_COLUMN))
+#define GTK_IS_COLUMNS_COLUMN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_COLUMN))
+#define GTK_SHORTCUTS_COLUMN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_COLUMN, 
GtkShortcutsColumnClass))
+
+
+typedef struct _GtkShortcutsColumn      GtkShortcutsColumn;
+typedef struct _GtkShortcutsColumnClass GtkShortcutsColumnClass;
+
+
+GType gtk_shortcuts_column_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUTS_COLUMN_H__ */
diff --git a/gtk/gtkshortcutsgesture.c b/gtk/gtkshortcutsgesture.c
new file mode 100644
index 0000000..8762ee2
--- /dev/null
+++ b/gtk/gtkshortcutsgesture.c
@@ -0,0 +1,211 @@
+ /* gtkshortcutsgesture.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutsgestureprivate.h"
+#include "gtkimage.h"
+#include "gtklabel.h"
+#include "gtksizegroup.h"
+#include "gtkorientable.h"
+#include "gtkstylecontext.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+struct _GtkShortcutsGesture
+{
+  GtkBox    parent_instance;
+
+  GtkImage *image;
+  GtkLabel *title;
+  GtkLabel *subtitle;
+  GtkBox   *desc_box;
+};
+
+struct _GtkShortcutsGestureClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsGesture, gtk_shortcuts_gesture, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_DESC_SIZE_GROUP,
+  PROP_GICON,
+  PROP_ICON_SIZE_GROUP,
+  PROP_SUBTITLE,
+  PROP_TITLE,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+gtk_shortcuts_gesture_set_gicon (GtkShortcutsGesture *self,
+                                 GIcon               *gicon)
+{
+  gtk_image_set_from_gicon (self->image, gicon, GTK_ICON_SIZE_DIALOG);
+}
+
+static void
+gtk_shortcuts_gesture_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GtkShortcutsGesture *self = GTK_SHORTCUTS_GESTURE (object);
+
+  switch (prop_id)
+    {
+    case PROP_SUBTITLE:
+      g_value_set_string (value, gtk_label_get_label (self->subtitle));
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, gtk_label_get_label (self->title));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_gesture_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GtkShortcutsGesture *self = GTK_SHORTCUTS_GESTURE (object);
+
+  switch (prop_id)
+    {
+    case PROP_DESC_SIZE_GROUP:
+      {
+        GtkSizeGroup *group = g_value_get_object (value);
+
+        if (group != NULL)
+          gtk_size_group_add_widget (group, GTK_WIDGET (self->desc_box));
+        break;
+      }
+
+    case PROP_GICON:
+      gtk_shortcuts_gesture_set_gicon (self, g_value_get_object (value));
+      break;
+
+    case PROP_ICON_SIZE_GROUP:
+      {
+        GtkSizeGroup *group = g_value_get_object (value);
+
+        if (group != NULL)
+          gtk_size_group_add_widget (group, GTK_WIDGET (self->image));
+        break;
+      }
+
+    case PROP_SUBTITLE:
+      gtk_label_set_label (self->subtitle, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE:
+      gtk_label_set_label (self->title, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_gesture_class_init (GtkShortcutsGestureClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gtk_shortcuts_gesture_get_property;
+  object_class->set_property = gtk_shortcuts_gesture_set_property;
+
+  properties[PROP_DESC_SIZE_GROUP] =
+    g_param_spec_object ("desc-size-group",
+                         P_("Description Size Group"),
+                         P_("Description Size Group"),
+                         GTK_TYPE_SIZE_GROUP,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_GICON] =
+    g_param_spec_object ("gicon",
+                         P_("GIcon"),
+                         P_("GIcon"),
+                         G_TYPE_ICON,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_ICON_SIZE_GROUP] =
+    g_param_spec_object ("icon-size-group",
+                         P_("Icon Size Group"),
+                         P_("Icon Size Group"),
+                         GTK_TYPE_SIZE_GROUP,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_SUBTITLE] =
+    g_param_spec_string ("subtitle",
+                         P_("Subtitle"),
+                         P_("Subtitle"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         P_("Title"),
+                         P_("Title"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+gtk_shortcuts_gesture_init (GtkShortcutsGesture *self)
+{
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
+  gtk_box_set_spacing (GTK_BOX (self), 12);
+
+  self->image = g_object_new (GTK_TYPE_IMAGE,
+                              "visible", TRUE,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->image));
+
+  self->desc_box = g_object_new (GTK_TYPE_BOX,
+                                 "hexpand", TRUE,
+                                 "orientation", GTK_ORIENTATION_VERTICAL,
+                                 "visible", TRUE,
+                                 NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->desc_box));
+
+  self->title = g_object_new (GTK_TYPE_LABEL,
+                              "visible", TRUE,
+                              "xalign", 0.0f,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self->desc_box), GTK_WIDGET (self->title));
+
+  self->subtitle = g_object_new (GTK_TYPE_LABEL,
+                                 "visible", TRUE,
+                                 "xalign", 0.0f,
+                                 NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self->subtitle)),
+                               "dim-label");
+  gtk_container_add (GTK_CONTAINER (self->desc_box), GTK_WIDGET (self->subtitle));
+}
diff --git a/gtk/gtkshortcutsgestureprivate.h b/gtk/gtkshortcutsgestureprivate.h
new file mode 100644
index 0000000..63ced78
--- /dev/null
+++ b/gtk/gtkshortcutsgestureprivate.h
@@ -0,0 +1,42 @@
+/* gtkshortcutsgestureprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_GESTURE_H__
+#define __GTK_SHORTCUTS_GESTURE_H__
+
+#include <gtk/gtkbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_GESTURE (gtk_shortcuts_gesture_get_type())
+#define GTK_SHORTCUTS_GESTURE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_SHORTCUTS_GESTURE, GtkShortcutsGesture))
+#define GTK_SHORTCUTS_GESTURE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_GESTURE, 
GtkShortcutsGestureClass))
+#define GTK_IS_SHORTCUTS_GESTURE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_SHORTCUTS_GESTURE))
+#define GTK_IS_SHORTCUTS_GESTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_GESTURE))
+#define GTK_SHORTCUTS_GESTURE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_GESTURE, 
GtkShortcutsGestureClass))
+
+
+typedef struct _GtkShortcutsGesture      GtkShortcutsGesture;
+typedef struct _GtkShortcutsGestureClass GtkShortcutsGestureClass;
+
+
+GType        gtk_shortcuts_gesture_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUTS_GESTURE_H__ */
diff --git a/gtk/gtkshortcutsgroup.c b/gtk/gtkshortcutsgroup.c
new file mode 100644
index 0000000..59c2012
--- /dev/null
+++ b/gtk/gtkshortcutsgroup.c
@@ -0,0 +1,123 @@
+/* gtkshortcutsgroup.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutsgroupprivate.h"
+
+#include "gtklabel.h"
+#include "gtkorientable.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+struct _GtkShortcutsGroup
+{
+  GtkBox    parent_instance;
+
+  GtkLabel *title;
+};
+
+struct _GtkShortcutsGroupClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsGroup, gtk_shortcuts_group, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+gtk_shortcuts_group_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      g_value_set_string (value, gtk_label_get_label (self->title));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_group_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      gtk_label_set_label (self->title, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_group_class_init (GtkShortcutsGroupClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gtk_shortcuts_group_get_property;
+  object_class->set_property = gtk_shortcuts_group_set_property;
+
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         P_("Title"),
+                         P_("Title"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+gtk_shortcuts_group_init (GtkShortcutsGroup *self)
+{
+  PangoAttrList *attrs;
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+  gtk_box_set_spacing (GTK_BOX (self), 10);
+
+  attrs = pango_attr_list_new ();
+  pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+  self->title = g_object_new (GTK_TYPE_LABEL,
+                              "attributes", attrs,
+                              "visible", TRUE,
+                              "xalign", 0.0f,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->title));
+  pango_attr_list_unref (attrs);
+}
diff --git a/gtk/gtkshortcutsgroupprivate.h b/gtk/gtkshortcutsgroupprivate.h
new file mode 100644
index 0000000..4b27ec4
--- /dev/null
+++ b/gtk/gtkshortcutsgroupprivate.h
@@ -0,0 +1,41 @@
+/* gtkshortcutsgroupprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_GROUP_H__
+#define __GTK_SHORTCUTS_GROUP_H__
+
+#include <gtk/gtkbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_GROUP            (gtk_shortcuts_group_get_type ())
+#define GTK_SHORTCUTS_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUTS_GROUP, 
GtkShortcutsGroup))
+#define GTK_SHORTCUTS_GROUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_GROUP, 
GtkShortcutsGroupClass))
+#define GTK_IS_SHORTCUTS_GROUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUTS_GROUP))
+#define GTK_IS_SHORTCUTS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_GROUP))
+#define GTK_SHORTCUTS_GROUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_GROUP, 
GtkShortcutsGroupClass))
+
+
+typedef struct _GtkShortcutsGroup         GtkShortcutsGroup;
+typedef struct _GtkShortcutsGroupClass    GtkShortcutsGroupClass;
+
+GType gtk_shortcuts_group_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUTS_GROUP_H__ */
diff --git a/gtk/gtkshortcutspage.c b/gtk/gtkshortcutspage.c
new file mode 100644
index 0000000..8208aeb
--- /dev/null
+++ b/gtk/gtkshortcutspage.c
@@ -0,0 +1,47 @@
+/* gtkshortcutspage.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutspageprivate.h"
+
+#include "gtkorientable.h"
+
+struct _GtkShortcutsPage
+{
+  GtkBox parent_instance;
+};
+
+struct _GtkShortcutsPageClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsPage, gtk_shortcuts_page, GTK_TYPE_BOX)
+
+static void
+gtk_shortcuts_page_class_init (GtkShortcutsPageClass *klass)
+{
+}
+
+static void
+gtk_shortcuts_page_init (GtkShortcutsPage *self)
+{
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
+  gtk_box_set_spacing (GTK_BOX (self), 22);
+}
diff --git a/gtk/gtkshortcutspageprivate.h b/gtk/gtkshortcutspageprivate.h
new file mode 100644
index 0000000..a08ec31
--- /dev/null
+++ b/gtk/gtkshortcutspageprivate.h
@@ -0,0 +1,42 @@
+/* gtkshortcutspageprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_PAGE_H__
+#define __GTK_SHORTCUTS_PAGE_H__
+
+#include <gtk/gtkbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_PAGE (gtk_shortcuts_page_get_type ())
+#define GTK_SHORTCUTS_PAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUTS_PAGE, 
GtkShortcutsPage))
+#define GTK_SHORTCUTS_PAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_PAGE, 
GtkShortcutsPageClass))
+#define GTK_IS_SHORTCUTS_PAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUTS_PAGE))
+#define GTK_IS_SHORTCUTS_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_PAGE))
+#define GTK_SHORTCUTS_PAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_PAGE, 
GtkShortcutsPageClass))
+
+
+typedef struct _GtkShortcutsPage      GtkShortcutsPage;
+typedef struct _GtkShortcutsPageClass GtkShortcutsPageClass;
+
+
+GType gtk_shortcuts_page_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUTS_PAGE_H__ */
diff --git a/gtk/gtkshortcutsshortcut.c b/gtk/gtkshortcutsshortcut.c
new file mode 100644
index 0000000..78c35ee
--- /dev/null
+++ b/gtk/gtkshortcutsshortcut.c
@@ -0,0 +1,174 @@
+/* gtkshortcutsshortcut.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutsshortcutprivate.h"
+
+#include "gtkshortcutlabelprivate.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+
+struct _GtkShortcutsShortcut
+{
+  GtkBox            parent_instance;
+
+  GtkShortcutLabel *accelerator;
+  GtkLabel         *title;
+};
+
+struct _GtkShortcutsShortcutClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsShortcut, gtk_shortcuts_shortcut, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_ACCELERATOR,
+  PROP_ACCELERATOR_SIZE_GROUP,
+  PROP_TITLE,
+  PROP_TITLE_SIZE_GROUP,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+gtk_shortcuts_shortcut_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GtkShortcutsShortcut *self = GTK_SHORTCUTS_SHORTCUT (object);
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      g_value_set_string (value, gtk_label_get_label (self->title));
+      break;
+
+    case PROP_ACCELERATOR:
+      g_value_set_string (value, gtk_shortcut_label_get_accelerator (self->accelerator));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_shortcut_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GtkShortcutsShortcut *self = GTK_SHORTCUTS_SHORTCUT (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACCELERATOR:
+      gtk_shortcut_label_set_accelerator (self->accelerator, g_value_get_string (value));
+      break;
+
+    case PROP_ACCELERATOR_SIZE_GROUP:
+      {
+        GtkSizeGroup *group = g_value_get_object (value);
+
+        if (group != NULL)
+          gtk_size_group_add_widget (group, GTK_WIDGET (self->accelerator));
+        break;
+      }
+
+    case PROP_TITLE:
+      gtk_label_set_label (self->title, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE_SIZE_GROUP:
+      {
+        GtkSizeGroup *group = g_value_get_object (value);
+
+        if (group != NULL)
+          gtk_size_group_add_widget (group, GTK_WIDGET (self->title));
+        break;
+      }
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_shortcut_class_init (GtkShortcutsShortcutClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gtk_shortcuts_shortcut_get_property;
+  object_class->set_property = gtk_shortcuts_shortcut_set_property;
+
+  properties[PROP_ACCELERATOR] =
+    g_param_spec_string ("accelerator",
+                         P_("Accelerator"),
+                         P_("Accelerator"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_ACCELERATOR_SIZE_GROUP] =
+    g_param_spec_object ("accelerator-size-group",
+                         P_("Accelerator Size Group"),
+                         P_("Accelerator Size Group"),
+                         GTK_TYPE_SIZE_GROUP,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         P_("Title"),
+                         P_("Title"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_TITLE_SIZE_GROUP] =
+    g_param_spec_object ("title-size-group",
+                         P_("Title Size Group"),
+                         P_("Title Size Group"),
+                         GTK_TYPE_SIZE_GROUP,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+gtk_shortcuts_shortcut_init (GtkShortcutsShortcut *self)
+{
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
+  gtk_box_set_spacing (GTK_BOX (self), 12);
+
+  self->accelerator = g_object_new (GTK_TYPE_SHORTCUT_LABEL,
+                                    "visible", TRUE,
+                                    NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->accelerator));
+
+  self->title = g_object_new (GTK_TYPE_LABEL,
+                              "hexpand", TRUE,
+                              "visible", TRUE,
+                              "xalign", 0.0f,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->title));
+}
diff --git a/gtk/gtkshortcutsshortcutprivate.h b/gtk/gtkshortcutsshortcutprivate.h
new file mode 100644
index 0000000..9c30445
--- /dev/null
+++ b/gtk/gtkshortcutsshortcutprivate.h
@@ -0,0 +1,42 @@
+/* gtkshortcutsshortcutprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_SHORTCUTS_SHORTCUT_H
+#define GTK_SHORTCUTS_SHORTCUT_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_SHORTCUT (gtk_shortcuts_shortcut_get_type())
+#define GTK_SHORTCUTS_SHORTCUT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_TYPE_SHORTCUTS_SHORTCUT, GtkShortcutsShortcut))
+#define GTK_SHORTCUTS_SHORTCUT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_TYPE_SHORTCUTS_SHORTCUT, GtkShortcutsShortcutClass))
+#define GTK_IS_SHORTCUTS_SHORTCUT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_TYPE_SHORTCUTS_SHORTCUT))
+#define GTK_IS_SHORTCUTS_SHORTCUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_TYPE_SHORTCUTS_SHORTCUT))
+#define GTK_SHORTCUTS_SHORTCUT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_TYPE_SHORTCUTS_SHORTCUT, GtkShortcutsShortcutClass))
+
+
+typedef struct _GtkShortcutsShortcut      GtkShortcutsShortcut;
+typedef struct _GtkShortcutsShortcutClass GtkShortcutsShortcutClass;
+
+
+GType        gtk_shortcuts_shortcut_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* GTK_SHORTCUTS_SHORTCUT_H */
diff --git a/gtk/gtkshortcutsview.c b/gtk/gtkshortcutsview.c
new file mode 100644
index 0000000..ce94f9a
--- /dev/null
+++ b/gtk/gtkshortcutsview.c
@@ -0,0 +1,231 @@
+/* gtkshortcutsview.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutsviewprivate.h"
+
+#include "gtkshortcutspageprivate.h"
+#include "gtkstack.h"
+#include "gtkstackswitcher.h"
+#include "gtkstylecontext.h"
+#include "gtkorientable.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+struct _GtkShortcutsView
+{
+  GtkBox            parent_instance;
+
+  gchar            *name;
+  gchar            *title;
+
+  GtkStack         *stack;
+  GtkStackSwitcher *switcher;
+
+  guint             last_page_num;
+};
+
+struct _GtkShortcutsViewClass
+{
+  GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShortcutsView, gtk_shortcuts_view, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  PROP_VIEW_NAME,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+
+static void
+adjust_page_buttons (GtkWidget *widget,
+                     gpointer   data)
+{
+  gint *count = data;
+
+  /*
+   * TODO: This is a hack to get the GtkStackSwitcher radio
+   *       buttons to look how we want. However, it's very
+   *       much font size specific.
+   */
+  gtk_widget_set_size_request (widget, 34, 34);
+
+  (*count)++;
+}
+
+static void
+gtk_shortcuts_view_add (GtkContainer *container,
+                        GtkWidget    *child)
+{
+  GtkShortcutsView *self = (GtkShortcutsView *)container;
+
+  if (GTK_IS_SHORTCUTS_PAGE (child))
+    {
+      gchar *title = NULL;
+      guint count = 0;
+
+      title = g_strdup_printf ("%u", ++self->last_page_num);
+      gtk_container_add_with_properties (GTK_CONTAINER (self->stack), child,
+                                         "title", title,
+                                         NULL);
+
+      gtk_container_foreach (GTK_CONTAINER (self->switcher), adjust_page_buttons, &count);
+      gtk_widget_set_visible (GTK_WIDGET (self->switcher), (count > 1));
+      g_free (title);
+    }
+  else
+    {
+      GTK_CONTAINER_CLASS (gtk_shortcuts_view_parent_class)->add (container, child);
+    }
+}
+
+static void
+gtk_shortcuts_view_finalize (GObject *object)
+{
+  GtkShortcutsView *self = (GtkShortcutsView *)object;
+
+  g_clear_pointer (&self->name, g_free);
+  g_clear_pointer (&self->title, g_free);
+
+  G_OBJECT_CLASS (gtk_shortcuts_view_parent_class)->finalize (object);
+}
+
+static void
+gtk_shortcuts_view_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GtkShortcutsView *self = (GtkShortcutsView *)object;
+
+  switch (prop_id)
+    {
+    case PROP_VIEW_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, self->title);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_view_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GtkShortcutsView *self = (GtkShortcutsView *)object;
+
+  switch (prop_id)
+    {
+    case PROP_VIEW_NAME:
+      g_free (self->name);
+      self->name = g_value_dup_string (value);
+      break;
+
+    case PROP_TITLE:
+      g_free (self->title);
+      self->title = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_view_class_init (GtkShortcutsViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->finalize = gtk_shortcuts_view_finalize;
+  object_class->get_property = gtk_shortcuts_view_get_property;
+  object_class->set_property = gtk_shortcuts_view_set_property;
+
+  container_class->add = gtk_shortcuts_view_add;
+
+  properties[PROP_VIEW_NAME] =
+    g_param_spec_string ("view-name",
+                         P_("View Name"),
+                         P_("View Name"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         P_("Title"),
+                         P_("Title"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+gtk_shortcuts_view_init (GtkShortcutsView *self)
+{
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+  gtk_box_set_homogeneous (GTK_BOX (self), FALSE);
+  gtk_box_set_spacing (GTK_BOX (self), 22);
+  gtk_container_set_border_width (GTK_CONTAINER (self), 24);
+
+  self->stack = g_object_new (GTK_TYPE_STACK,
+                              "homogeneous", TRUE,
+                              "transition-type", GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT,
+                              "vexpand", TRUE,
+                              "visible", TRUE,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->stack));
+
+  self->switcher = g_object_new (GTK_TYPE_STACK_SWITCHER,
+                                 "halign", GTK_ALIGN_CENTER,
+                                 "stack", self->stack,
+                                 "spacing", 12,
+                                 "no-show-all", TRUE,
+                                 NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self->switcher)), "round");
+  gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (self->switcher)), "linked");
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->switcher));
+}
+
+const gchar *
+gtk_shortcuts_view_get_view_name (GtkShortcutsView *self)
+{
+  g_return_val_if_fail (GTK_IS_SHORTCUTS_VIEW (self), NULL);
+
+  return self->name;
+}
+
+const gchar *
+gtk_shortcuts_view_get_title (GtkShortcutsView *self)
+{
+  g_return_val_if_fail (GTK_IS_SHORTCUTS_VIEW (self), NULL);
+
+  return self->title;
+}
diff --git a/gtk/gtkshortcutsviewprivate.h b/gtk/gtkshortcutsviewprivate.h
new file mode 100644
index 0000000..ed1145d
--- /dev/null
+++ b/gtk/gtkshortcutsviewprivate.h
@@ -0,0 +1,45 @@
+/* gtkshortcutsviewprivate.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_VIEW_H__
+#define __GTK_SHORTCUTS_VIEW_H__
+
+#include <gtk/gtkbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_VIEW (gtk_shortcuts_view_get_type ())
+#define GTK_SHORTCUTS_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUTS_VIEW, 
GtkShortcutsView))
+#define GTK_SHORTCUTS_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_VIEW, 
GtkShortcutsViewClass))
+#define GTK_IS_SHORTCUTS_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUTS_VIEW))
+#define GTK_IS_SHORTCUTS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_VIEW))
+#define GTK_SHORTCUTS_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_VIEW, 
GtkShortcutsViewClass))
+
+
+typedef struct _GtkShortcutsView      GtkShortcutsView;
+typedef struct _GtkShortcutsViewClass GtkShortcutsViewClass;
+
+
+GType        gtk_shortcuts_view_get_type (void) G_GNUC_CONST;
+
+const gchar *gtk_shortcuts_view_get_view_name (GtkShortcutsView *self);
+const gchar *gtk_shortcuts_view_get_title     (GtkShortcutsView *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SHORTCUTS_VIEW_H__ */
diff --git a/gtk/gtkshortcutswindow.c b/gtk/gtkshortcutswindow.c
new file mode 100644
index 0000000..37ccc7c
--- /dev/null
+++ b/gtk/gtkshortcutswindow.c
@@ -0,0 +1,970 @@
+/* gtkshortcutswindow.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkshortcutswindow.h"
+#include "gtkscrolledwindow.h"
+#include "gtkshortcutscolumnprivate.h"
+#include "gtkshortcutsgestureprivate.h"
+#include "gtkshortcutsgroupprivate.h"
+#include "gtkshortcutspageprivate.h"
+#include "gtkshortcutsshortcutprivate.h"
+#include "gtkshortcutsviewprivate.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+
+typedef struct
+{
+  GHashTable     *keywords;
+  gchar          *initial_view;
+  gchar          *last_view_name;
+  GtkSizeGroup   *search_text_group;
+  GtkSizeGroup   *search_image_group;
+  GHashTable     *search_items_hash;
+
+  GtkStack       *stack;
+  GtkMenuButton  *menu_button;
+  GtkLabel       *menu_label;
+  GtkSearchBar   *search_bar;
+  GtkHeaderBar   *header_bar;
+  GtkPopover     *popover;
+  GtkListBox     *list_box;
+  GtkBox         *search_gestures;
+  GtkBox         *search_shortcuts;
+} GtkShortcutsWindowPrivate;
+
+typedef struct
+{
+  GtkShortcutsWindow *self;
+  GtkBuilder        *builder;
+  GQueue            *stack;
+  GtkWidget         *search_item;
+  GQueue            *column_image_size_groups;
+  GQueue            *column_desc_size_groups;
+  gchar             *property_name;
+  guint              translatable : 1;
+} ViewsParserData;
+
+static void gtk_buildable_iface_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GtkShortcutsWindow, gtk_shortcuts_window, GTK_TYPE_WINDOW, 0,
+                        G_ADD_PRIVATE (GtkShortcutsWindow)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_buildable_iface_init))
+
+enum {
+  CLOSE,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_0,
+  PROP_VIEW_NAME,
+  LAST_PROP
+};
+
+static GParamSpec *properties[LAST_PROP];
+static guint signals[LAST_SIGNAL];
+
+static void
+gtk_shortcuts_window_add_view (GtkShortcutsWindow *self,
+                               GtkShortcutsView   *view)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  GtkListBoxRow *row;
+  const gchar *title;
+  const gchar *name;
+  GtkWidget *label;
+
+  name = gtk_shortcuts_view_get_view_name (view);
+  title = gtk_shortcuts_view_get_title (view);
+
+  gtk_stack_add_titled (priv->stack, GTK_WIDGET (view), name, title);
+
+  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                      "visible", TRUE,
+                      NULL);
+  g_object_set_data_full (G_OBJECT (row), "GTK_SHORTCUTS_VIEW_NAME", g_strdup (name), g_free);
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "margin", 6,
+                        "label", title,
+                        "xalign", 0.5f,
+                        "visible", TRUE,
+                        NULL);
+  gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (label));
+  gtk_container_add (GTK_CONTAINER (priv->list_box), GTK_WIDGET (row));
+}
+
+static void
+gtk_shortcuts_window_add (GtkContainer *container,
+                         GtkWidget    *widget)
+{
+  GtkShortcutsWindow *self = (GtkShortcutsWindow *)container;
+
+  if (GTK_IS_SHORTCUTS_VIEW (widget))
+    gtk_shortcuts_window_add_view (self, GTK_SHORTCUTS_VIEW (widget));
+  else
+    GTK_CONTAINER_CLASS (gtk_shortcuts_window_parent_class)->add (container, widget);
+}
+
+static void
+gtk_shortcuts_window__stack__notify_visible_child (GtkShortcutsWindow *self,
+                                                   GParamSpec         *pspec,
+                                                   GtkStack           *stack)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  GtkWidget *visible_child;
+
+  visible_child = gtk_stack_get_visible_child (stack);
+
+  if (GTK_IS_SHORTCUTS_VIEW (visible_child))
+    {
+      const gchar *title;
+
+      title = gtk_shortcuts_view_get_title (GTK_SHORTCUTS_VIEW (visible_child));
+      gtk_label_set_label (priv->menu_label, title);
+    }
+  else if (visible_child != NULL)
+    {
+      gchar *title = NULL;
+
+      gtk_container_child_get (GTK_CONTAINER (stack), visible_child,
+                               "title", &title,
+                               NULL);
+      gtk_label_set_label (priv->menu_label, title);
+      g_free (title);
+    }
+}
+
+static void
+gtk_shortcuts_window__list_box__row_activated (GtkShortcutsWindow *self,
+                                               GtkListBoxRow      *row,
+                                               GtkListBox         *list_box)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  const gchar *name;
+
+  name = g_object_get_data (G_OBJECT (row), "GTK_SHORTCUTS_VIEW_NAME");
+  gtk_stack_set_visible_child_name (priv->stack, name);
+  gtk_widget_hide (GTK_WIDGET (priv->popover));
+}
+
+static void
+gtk_shortcuts_window_add_search_item (GtkShortcutsWindow *self,
+                                      GtkWidget          *search_item)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  GString *str;
+  gchar *downcase;
+
+  str = g_string_new (NULL);
+
+  if (GTK_IS_SHORTCUTS_SHORTCUT (search_item))
+    {
+      gchar *accelerator = NULL;
+      gchar *title = NULL;
+      gchar *hash_key = NULL;
+
+      g_object_get (search_item,
+                    "accelerator", &accelerator,
+                    "title", &title,
+                    NULL);
+
+      hash_key = g_strdup_printf ("%s-%s", title, hash_key);
+      if (g_hash_table_contains (priv->search_items_hash, hash_key))
+        {
+          g_free (hash_key);
+          g_free (title);
+          g_free (accelerator);
+
+          return;
+        }
+
+      g_hash_table_insert (priv->search_items_hash, hash_key, GINT_TO_POINTER (1));
+
+      g_object_set (search_item,
+                    "accelerator-size-group", priv->search_image_group,
+                    "title-size-group", priv->search_text_group,
+                    NULL);
+
+      g_string_append_printf (str, "%s %s", accelerator, title);
+
+      gtk_container_add (GTK_CONTAINER (priv->search_shortcuts), search_item);
+
+      g_free (title);
+      g_free (accelerator);
+    }
+  else if (GTK_IS_SHORTCUTS_GESTURE (search_item))
+    {
+      gchar *subtitle = NULL;
+      gchar *title = NULL;
+      gchar *hash_key = NULL;
+
+      g_object_get (search_item,
+                    "subtitle", &subtitle,
+                    "title", &title,
+                    NULL);
+
+      hash_key = g_strdup_printf ("%s-%s", title, hash_key);
+      if (g_hash_table_contains (priv->search_items_hash, hash_key))
+        {
+          g_free (subtitle);
+          g_free (title);
+          g_free (hash_key);
+
+          return;
+        }
+
+      g_hash_table_insert (priv->search_items_hash, hash_key, GINT_TO_POINTER (1));
+
+      g_object_set (search_item,
+                    "icon-size-group", priv->search_image_group,
+                    "desc-size-group", priv->search_text_group,
+                    NULL);
+
+      g_string_append_printf (str, "%s %s", title, subtitle);
+
+      gtk_container_add (GTK_CONTAINER (priv->search_gestures), search_item);
+
+      g_free (subtitle);
+      g_free (title);
+    }
+
+  downcase = g_utf8_strdown (str->str, str->len);
+  g_hash_table_insert (priv->keywords, search_item, downcase);
+  g_string_free (str, TRUE);
+}
+
+static void
+gtk_shortcuts_window__entry__changed (GtkShortcutsWindow *self,
+                                     GtkSearchEntry      *search_entry)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  gchar *downcase = NULL;
+  GHashTableIter iter;
+  const gchar *text;
+  const gchar *last_view_name;
+  gpointer key;
+  gpointer value;
+
+  text = gtk_entry_get_text (GTK_ENTRY (search_entry));
+
+  if (!text || !*text)
+    {
+      if (priv->last_view_name != NULL)
+        {
+          gtk_stack_set_visible_child_name (priv->stack, priv->last_view_name);
+          return;
+        }
+    }
+
+  last_view_name = gtk_stack_get_visible_child_name (priv->stack);
+
+  if (g_strcmp0 (last_view_name, "internal-search") != 0)
+    {
+      g_free (priv->last_view_name);
+      priv->last_view_name = g_strdup (last_view_name);
+    }
+
+  gtk_stack_set_visible_child_name (priv->stack, "internal-search");
+
+  downcase = g_utf8_strdown (text, -1);
+
+  g_hash_table_iter_init (&iter, priv->keywords);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GtkWidget *widget = key;
+      const gchar *keywords = value;
+
+      gtk_widget_set_visible (widget, strstr (keywords, downcase) != NULL);
+    }
+
+  g_free (downcase);
+}
+
+static gboolean
+check_parent (GMarkupParseContext  *context,
+              const gchar          *element_name,
+              GError              **error)
+{
+  const GSList *stack;
+  const gchar *parent_name;
+  const gchar *our_name;
+
+  stack = g_markup_parse_context_get_element_stack (context);
+  our_name = stack->data;
+  parent_name = stack->next ? stack->next->data : "";
+
+  if (g_strcmp0 (parent_name, element_name) != 0)
+    {
+      gint line;
+      gint col;
+
+      g_markup_parse_context_get_position (context, &line, &col);
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_TAG,
+                   "%d:%d: Element <%s> found in <%s>, expected <%s>.",
+                   line, col, our_name, parent_name, element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+views_parser_start_element (GMarkupParseContext  *context,
+                            const gchar          *element_name,
+                            const gchar         **attribute_names,
+                            const gchar         **attribute_values,
+                            gpointer              user_data,
+                            GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GtkWidget *item;
+
+  if (g_strcmp0 (element_name, "views") == 0)
+    {
+    }
+  else if (g_strcmp0 (element_name, "view") == 0)
+    {
+      const gchar *name = NULL;
+
+      if (!check_parent (context, "views", error))
+        return;
+
+      if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                        G_MARKUP_COLLECT_STRING, "name", &name,
+                                        G_MARKUP_COLLECT_INVALID))
+        return;
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_VIEW,
+                           "view-name", name,
+                           "visible", TRUE,
+                           NULL);
+
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "page") == 0)
+    {
+      if (!check_parent (context, "view", error))
+        return;
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_PAGE,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "column") == 0)
+    {
+      GtkSizeGroup *size_group;
+
+      if (!check_parent (context, "page", error))
+        return;
+
+      size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+      g_queue_push_head (parser_data->column_image_size_groups, size_group);
+
+      size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+      g_queue_push_head (parser_data->column_desc_size_groups, size_group);
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_COLUMN,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "group") == 0)
+    {
+      if (!check_parent (context, "column", error))
+        return;
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_GROUP,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "shortcut") == 0)
+    {
+      GtkSizeGroup *accel_size_group;
+      GtkSizeGroup *desc_size_group;
+
+      if (!check_parent (context, "group", error))
+        return;
+
+      accel_size_group = g_queue_peek_head (parser_data->column_image_size_groups);
+      desc_size_group = g_queue_peek_head (parser_data->column_desc_size_groups);
+
+      parser_data->search_item = g_object_new (GTK_TYPE_SHORTCUTS_SHORTCUT,
+                                               "visible", TRUE,
+                                               NULL);
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_SHORTCUT,
+                           "accelerator-size-group", accel_size_group,
+                           "title-size-group", desc_size_group,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "gesture") == 0)
+    {
+      GtkSizeGroup *accel_size_group;
+      GtkSizeGroup *desc_size_group;
+
+      if (!check_parent (context, "group", error))
+        return;
+
+      accel_size_group = g_queue_peek_head (parser_data->column_image_size_groups);
+      desc_size_group = g_queue_peek_head (parser_data->column_desc_size_groups);
+
+      parser_data->search_item = g_object_new (GTK_TYPE_SHORTCUTS_GESTURE,
+                                               "visible", TRUE,
+                                               NULL);
+
+      item = g_object_new (GTK_TYPE_SHORTCUTS_GESTURE,
+                           "desc-size-group", desc_size_group,
+                           "icon-size-group", accel_size_group,
+                           "visible", TRUE,
+                           NULL);
+      g_queue_push_head (parser_data->stack, g_object_ref_sink (item));
+    }
+  else if (g_strcmp0 (element_name, "property") == 0)
+    {
+      const gchar *name = NULL;
+      const gchar *translatable = NULL;
+
+      item = g_queue_peek_head (parser_data->stack);
+
+      if (item == NULL)
+        {
+          g_set_error (error,
+                       GTK_BUILDER_ERROR,
+                       GTK_BUILDER_ERROR_INVALID_TAG,
+                       "Property called without a parent object");
+          return;
+        }
+
+      if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
+                                        G_MARKUP_COLLECT_STRING, "name", &name,
+                                        G_MARKUP_COLLECT_OPTIONAL | G_MARKUP_COLLECT_STRING, "translatable", 
&translatable,
+                                        G_MARKUP_COLLECT_INVALID))
+        return;
+
+      g_free (parser_data->property_name);
+      parser_data->property_name = g_strdup (name);
+      parser_data->translatable = (g_strcmp0 (translatable, "yes") == 0);
+    }
+  else
+    {
+      const GSList *stack;
+      const gchar *parent_name;
+      const gchar *our_name;
+      gint line;
+      gint col;
+
+      stack = g_markup_parse_context_get_element_stack (context);
+      our_name = stack->data;
+      parent_name = stack->next ? stack->next->data : "";
+
+      g_markup_parse_context_get_position (context, &line, &col);
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_TAG,
+                   "%d:%d: Unknown element <%s> found in <%s>.",
+                   line, col, our_name, parent_name);
+    }
+}
+
+static void
+views_parser_end_element (GMarkupParseContext  *context,
+                          const gchar          *element_name,
+                          gpointer              user_data,
+                          GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GtkWidget *item;
+
+  if (g_strcmp0 (element_name, "view") == 0)
+    {
+      item = g_queue_pop_head (parser_data->stack);
+      gtk_shortcuts_window_add_view (parser_data->self, GTK_SHORTCUTS_VIEW (item));
+      g_object_unref (item);
+    }
+  else if ((g_strcmp0 (element_name, "page") == 0) ||
+           (g_strcmp0 (element_name, "column") == 0) ||
+           (g_strcmp0 (element_name, "group") == 0) ||
+           (g_strcmp0 (element_name, "shortcut") == 0) ||
+           (g_strcmp0 (element_name, "gesture") == 0))
+    {
+      GtkWidget *parent;
+
+      item = g_queue_pop_head (parser_data->stack);
+      parent = g_queue_peek_head (parser_data->stack);
+      if (item != NULL && parent != NULL)
+        gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (item));
+      g_clear_object (&item);
+
+      if ((g_strcmp0 (element_name, "shortcut") == 0) ||
+          (g_strcmp0 (element_name, "gesture") == 0))
+        {
+          gtk_shortcuts_window_add_search_item (parser_data->self, parser_data->search_item);
+          parser_data->search_item = NULL;
+        }
+
+      if (g_strcmp0 (element_name, "column") == 0)
+        {
+          GtkSizeGroup *size_group;
+
+          size_group = g_queue_pop_head (parser_data->column_image_size_groups);
+          g_clear_object (&size_group);
+
+          size_group = g_queue_pop_head (parser_data->column_desc_size_groups);
+          g_clear_object (&size_group);
+        }
+    }
+  else if (g_strcmp0 (element_name, "property") == 0)
+    {
+      g_clear_pointer (&parser_data->property_name, g_free);
+    }
+}
+
+static void
+views_parser_text (GMarkupParseContext  *context,
+                   const gchar          *text,
+                   gsize                 text_len,
+                   gpointer              user_data,
+                   GError              **error)
+{
+  ViewsParserData *parser_data = user_data;
+  GParamSpec *pspec;
+  GtkWidget *item;
+  GValue value = { 0 };
+
+  if (parser_data->property_name == NULL)
+    return;
+
+  item = g_queue_peek_head (parser_data->stack);
+
+  if (item == NULL)
+    return;
+
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (item), parser_data->property_name);
+
+  if (pspec == NULL)
+    {
+      g_set_error (error,
+                   GTK_BUILDER_ERROR,
+                   GTK_BUILDER_ERROR_INVALID_PROPERTY,
+                   "No such property: %s",
+                   parser_data->property_name);
+      return;
+    }
+
+  if (parser_data->translatable)
+    text = _(text);
+
+  if (g_type_is_a (pspec->value_type, G_TYPE_OBJECT))
+    {
+      GObject *relative;
+
+      relative = gtk_builder_get_object (parser_data->builder, text);
+
+      if (relative == NULL)
+        {
+          g_set_error (error,
+                       GTK_BUILDER_ERROR,
+                       GTK_BUILDER_ERROR_INVALID_VALUE,
+                       "Unknown object for property '%s': %s",
+                       parser_data->property_name,
+                       text);
+          return;
+        }
+
+      g_value_init (&value, pspec->value_type);
+      g_value_set_object (&value, relative);
+    }
+  else if (!gtk_builder_value_from_string (parser_data->builder,
+                                           pspec, text, &value, error))
+    return;
+
+  if (parser_data->search_item != NULL)
+    g_object_set_property (G_OBJECT (parser_data->search_item),
+                           parser_data->property_name,
+                           &value);
+  g_object_set_property (G_OBJECT (item), parser_data->property_name, &value);
+  g_value_unset (&value);
+}
+
+static GMarkupParser ViewsParser = {
+  views_parser_start_element,
+  views_parser_end_element,
+  views_parser_text,
+};
+
+static gboolean
+gtk_shortcuts_window_custom_tag_start (GtkBuildable  *buildable,
+                                      GtkBuilder    *builder,
+                                      GObject       *child,
+                                      const gchar   *tagname,
+                                      GMarkupParser *parser,
+                                      gpointer      *data)
+{
+  if (g_strcmp0 (tagname, "views") == 0)
+    {
+      ViewsParserData *parser_data;
+
+      parser_data = g_slice_new0 (ViewsParserData);
+      parser_data->self = g_object_ref (buildable);
+      parser_data->builder = g_object_ref (builder);
+      parser_data->stack = g_queue_new ();
+      parser_data->column_image_size_groups = g_queue_new ();
+      parser_data->column_desc_size_groups = g_queue_new ();
+
+      *parser = ViewsParser;
+      *data = parser_data;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_shortcuts_window_custom_finished (GtkBuildable *buildable,
+                                     GtkBuilder   *builder,
+                                     GObject      *child,
+                                     const gchar  *tagname,
+                                     gpointer      user_data)
+{
+  if (g_strcmp0 (tagname, "views") == 0)
+    {
+      ViewsParserData *parser_data = user_data;
+
+      g_object_unref (parser_data->self);
+      g_object_unref (parser_data->builder);
+      g_queue_free_full (parser_data->stack, (GDestroyNotify)g_object_unref);
+      g_queue_free_full (parser_data->column_image_size_groups, (GDestroyNotify)g_object_unref);
+      g_queue_free_full (parser_data->column_desc_size_groups, (GDestroyNotify)g_object_unref);
+      g_slice_free (ViewsParserData, parser_data);
+    }
+}
+
+static void
+gtk_shortcuts_window_real_close (GtkShortcutsWindow *self)
+{
+  gtk_window_close (GTK_WINDOW (self));
+}
+
+static void
+gtk_shortcuts_window_constructed (GObject *object)
+{
+  GtkShortcutsWindow *self = (GtkShortcutsWindow *)object;
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+
+  G_OBJECT_CLASS (gtk_shortcuts_window_parent_class)->constructed (object);
+
+  if (priv->initial_view != NULL)
+    gtk_stack_set_visible_child_name (priv->stack, priv->initial_view);
+}
+
+static void
+gtk_shortcuts_window_finalize (GObject *object)
+{
+  GtkShortcutsWindow *self = (GtkShortcutsWindow *)object;
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+
+  g_clear_pointer (&priv->keywords, g_hash_table_unref);
+  g_clear_pointer (&priv->initial_view, g_free);
+  g_clear_pointer (&priv->last_view_name, g_free);
+  g_clear_pointer (&priv->search_items_hash, g_hash_table_unref);
+
+  g_clear_object (&priv->search_image_group);
+  g_clear_object (&priv->search_text_group);
+
+  G_OBJECT_CLASS (gtk_shortcuts_window_parent_class)->finalize (object);
+}
+
+static void
+gtk_buildable_iface_init (GtkBuildableIface *iface)
+{
+  iface->custom_tag_start = gtk_shortcuts_window_custom_tag_start;
+  iface->custom_finished = gtk_shortcuts_window_custom_finished;
+}
+
+static void
+gtk_shortcuts_window_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GtkShortcutsWindow *self = (GtkShortcutsWindow *)object;
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_VIEW_NAME:
+      {
+        GtkWidget *child = gtk_stack_get_visible_child (priv->stack);
+
+        if (child != NULL)
+          {
+            gchar *name = NULL;
+
+            gtk_container_child_get (GTK_CONTAINER (priv->stack), child,
+                                     "name", &name,
+                                     NULL);
+            g_value_take_string (value, name);
+          }
+      }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_window_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GtkShortcutsWindow *self = (GtkShortcutsWindow *)object;
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_VIEW_NAME:
+      g_free (priv->initial_view);
+      priv->initial_view = g_value_dup_string (value);
+      gtk_stack_set_visible_child_name (priv->stack, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_shortcuts_window_class_init (GtkShortcutsWindowClass *klass)
+{
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+  GtkBindingSet *binding_set = gtk_binding_set_by_class (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = gtk_shortcuts_window_constructed;
+  object_class->finalize = gtk_shortcuts_window_finalize;
+  object_class->get_property = gtk_shortcuts_window_get_property;
+  object_class->set_property = gtk_shortcuts_window_set_property;
+
+  container_class->add = gtk_shortcuts_window_add;
+
+  klass->close = gtk_shortcuts_window_real_close;
+
+  properties[PROP_VIEW_NAME] =
+    g_param_spec_string ("view-name",
+                         P_("View Name"),
+                         P_("View Name"),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  signals[CLOSE] = g_signal_new (I_("close"),
+                                 G_TYPE_FROM_CLASS (klass),
+                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                                 G_STRUCT_OFFSET (GtkShortcutsWindowClass, close),
+                                 NULL, NULL, NULL,
+                                 G_TYPE_NONE,
+                                 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
+
+  g_type_ensure (GTK_TYPE_SHORTCUTS_PAGE);
+  g_type_ensure (GTK_TYPE_SHORTCUTS_COLUMN);
+  g_type_ensure (GTK_TYPE_SHORTCUTS_GROUP);
+  g_type_ensure (GTK_TYPE_SHORTCUTS_GESTURE);
+  g_type_ensure (GTK_TYPE_SHORTCUTS_SHORTCUT);
+}
+
+static gboolean
+window_key_press_event_cb (GtkWidget *window,
+                           GdkEvent  *event,
+                           gpointer   data)
+{
+  GtkShortcutsWindow *self = GTK_SHORTCUTS_WINDOW (window);
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+
+  return gtk_search_bar_handle_event (priv->search_bar, event);
+}
+
+static void
+gtk_shortcuts_window_init (GtkShortcutsWindow *self)
+{
+  GtkShortcutsWindowPrivate *priv = gtk_shortcuts_window_get_instance_private (self);
+  GtkToggleButton *search_button;
+  GtkBox *main_box;
+  GtkBox *menu_box;
+  GtkBox *box;
+  GtkArrow *arrow;
+  GtkWidget *entry;
+  GtkWidget *scroller;
+
+  gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+
+  g_signal_connect (self, "key-press-event",
+                    G_CALLBACK (window_key_press_event_cb), NULL);
+
+  priv->keywords = g_hash_table_new_full (NULL, NULL, NULL, g_free);
+  priv->search_items_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  priv->search_text_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+  priv->search_image_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+  priv->header_bar = g_object_new (GTK_TYPE_HEADER_BAR,
+                                   "show-close-button", TRUE,
+                                   "visible", TRUE,
+                                   NULL);
+  gtk_window_set_titlebar (GTK_WINDOW (self), GTK_WIDGET (priv->header_bar));
+
+  search_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+                                "child", g_object_new (GTK_TYPE_IMAGE,
+                                                       "visible", TRUE,
+                                                       "icon-name", "edit-find-symbolic",
+                                                       NULL),
+                                "visible", TRUE,
+                                NULL);
+  gtk_container_add (GTK_CONTAINER (priv->header_bar), GTK_WIDGET (search_button));
+
+  main_box = g_object_new (GTK_TYPE_BOX,
+                           "orientation", GTK_ORIENTATION_VERTICAL,
+                           "visible", TRUE,
+                           NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (main_box));
+
+  priv->search_bar = g_object_new (GTK_TYPE_SEARCH_BAR,
+                                   "visible", TRUE,
+                                   NULL);
+  g_object_bind_property (priv->search_bar, "search-mode-enabled",
+                          search_button, "active",
+                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+  gtk_container_add (GTK_CONTAINER (main_box), GTK_WIDGET (priv->search_bar));
+
+  priv->stack = g_object_new (GTK_TYPE_STACK,
+                              "expand", TRUE,
+                              "homogeneous", TRUE,
+                              "transition-type", GTK_STACK_TRANSITION_TYPE_CROSSFADE,
+                              "visible", TRUE,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (main_box), GTK_WIDGET (priv->stack));
+
+  priv->menu_button = g_object_new (GTK_TYPE_MENU_BUTTON,
+                                    "focus-on-click", FALSE,
+                                    "visible", TRUE,
+                                    NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (priv->menu_button)),
+                               "flat");
+  gtk_header_bar_set_custom_title (priv->header_bar, GTK_WIDGET (priv->menu_button));
+
+  menu_box = g_object_new (GTK_TYPE_BOX,
+                           "orientation", GTK_ORIENTATION_HORIZONTAL,
+                           "spacing", 6,
+                           "visible", TRUE,
+                           NULL);
+  gtk_container_add (GTK_CONTAINER (priv->menu_button), GTK_WIDGET (menu_box));
+
+  priv->menu_label = g_object_new (GTK_TYPE_LABEL,
+                                   "visible", TRUE,
+                                   NULL);
+  gtk_container_add (GTK_CONTAINER (menu_box), GTK_WIDGET (priv->menu_label));
+
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  arrow = g_object_new (GTK_TYPE_ARROW,
+                        "arrow-type", GTK_ARROW_DOWN,
+                        "visible", TRUE,
+                        NULL);
+  gtk_container_add (GTK_CONTAINER (menu_box), GTK_WIDGET (arrow));
+  G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  priv->popover = g_object_new (GTK_TYPE_POPOVER,
+                                "border-width", 6,
+                                "relative-to", priv->menu_button,
+                                "position", GTK_POS_BOTTOM,
+                                NULL);
+  gtk_menu_button_set_popover (priv->menu_button, GTK_WIDGET (priv->popover));
+
+  priv->list_box = g_object_new (GTK_TYPE_LIST_BOX,
+                                 "selection-mode", GTK_SELECTION_NONE,
+                                 "visible", TRUE,
+                                 NULL);
+  g_signal_connect_object (priv->list_box,
+                           "row-activated",
+                           G_CALLBACK (gtk_shortcuts_window__list_box__row_activated),
+                           self,
+                           G_CONNECT_SWAPPED);
+  gtk_container_add (GTK_CONTAINER (priv->popover), GTK_WIDGET (priv->list_box));
+
+  entry = gtk_search_entry_new ();
+  gtk_widget_show (entry);
+  gtk_container_add (GTK_CONTAINER (priv->search_bar), entry);
+  g_object_set (entry,
+                "placeholder-text", _("Search Shortcuts"),
+                "width-chars", 40,
+                NULL);
+  g_signal_connect_object (entry,
+                           "search-changed",
+                           G_CALLBACK (gtk_shortcuts_window__entry__changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (priv->stack,
+                           "notify::visible-child",
+                           G_CALLBACK (gtk_shortcuts_window__stack__notify_visible_child),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                           "visible", TRUE,
+                           NULL);
+  box = g_object_new (GTK_TYPE_BOX,
+                      "border-width", 24,
+                      "halign", GTK_ALIGN_CENTER,
+                      "spacing", 24,
+                      "orientation", GTK_ORIENTATION_VERTICAL,
+                      "visible", TRUE,
+                      NULL);
+  gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (box));
+  gtk_stack_add_titled (priv->stack, scroller,
+                        "internal-search", _("Search Results"));
+
+  priv->search_shortcuts = g_object_new (GTK_TYPE_BOX,
+                                         "halign", GTK_ALIGN_CENTER,
+                                         "spacing", 6,
+                                         "orientation", GTK_ORIENTATION_VERTICAL,
+                                         "visible", TRUE,
+                                         NULL);
+  gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (priv->search_shortcuts));
+
+  priv->search_gestures = g_object_new (GTK_TYPE_BOX,
+                                        "halign", GTK_ALIGN_CENTER,
+                                        "spacing", 6,
+                                        "orientation", GTK_ORIENTATION_VERTICAL,
+                                        "visible", TRUE,
+                                        NULL);
+  gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (priv->search_gestures));
+}
diff --git a/gtk/gtkshortcutswindow.h b/gtk/gtkshortcutswindow.h
new file mode 100644
index 0000000..729e318
--- /dev/null
+++ b/gtk/gtkshortcutswindow.h
@@ -0,0 +1,55 @@
+/* gtkshortcutswindow.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_SHORTCUTS_WINDOW_H__
+#define __GTK_SHORTCUTS_WINDOW_H__
+
+#include <gtk/gtkwindow.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHORTCUTS_WINDOW            (gtk_shortcuts_window_get_type ())
+#define GTK_SHORTCUTS_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SHORTCUTS_WINDOW, 
GtkShortcutsWindow))
+#define GTK_SHORTCUTS_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SHORTCUTS_WINDOW, 
GtkShortcutsWindowClass))
+#define GTK_IS_SHORTCUTS_WINDOW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SHORTCUTS_WINDOW))
+#define GTK_IS_SHORTCUTS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SHORTCUTS_WINDOW))
+#define GTK_SHORTCUTS_WINDOW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SHORTCUTS_WINDOW, 
GtkShortcutsWindowClass))
+
+
+typedef struct _GtkShortcutsWindow         GtkShortcutsWindow;
+typedef struct _GtkShortcutsWindowClass    GtkShortcutsWindowClass;
+
+
+struct _GtkShortcutsWindow
+{
+  GtkWindow window;
+};
+
+struct _GtkShortcutsWindowClass
+{
+  GtkWindowClass parent_class;
+
+  void (*close) (GtkShortcutsWindow *self);
+};
+
+GType gtk_shortcuts_window_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* GTK_SHORTCUTS_WINDOW _H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]