[glib/wip/menus: 14/61] introduce GMenuModel and friends



commit 7af9be6b826698b0207b957a2e3799b94d58ed8d
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Jul 11 02:02:27 2011 +0200

    introduce GMenuModel and friends

 gio/Makefile.am   |   10 +-
 gio/gio.h         |    3 +
 gio/gmenu.c       |  674 +++++++++++++++++++++++++++++++++++++++++
 gio/gmenu.h       |  177 +++++++++++
 gio/gmenumarkup.c |  482 +++++++++++++++++++++++++++++
 gio/gmenumarkup.h |   23 ++
 gio/gmenumodel.c  |  873 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gmenumodel.h  |  243 +++++++++++++++
 8 files changed, 2483 insertions(+), 2 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index ed78a22..7e83fd3 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -132,7 +132,10 @@ application_headers = \
 	gdbusactiongroup.h		\
 	gactiongroupexporter.h		\
 	gapplicationcommandline.h	\
-	gapplication.h
+	gapplication.h			\
+	gmenumodel.h			\
+	gmenu.h				\
+	gmenumarkup.h
 
 application_sources = \
 	gactiongroup.c				\
@@ -144,7 +147,10 @@ application_sources = \
 	gapplicationcommandline.c		\
 	gapplicationimpl.h			\
 	gapplicationimpl-dbus.c			\
-	gapplication.c
+	gapplication.c				\
+	gmenumodel.c				\
+	gmenu.c					\
+	gmenumarkup.c
 
 local_sources = \
 	glocaldirectorymonitor.c 	\
diff --git a/gio/gio.h b/gio/gio.h
index cfd2452..6f91e94 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -146,6 +146,9 @@
 #include <gio/gdbusobjectmanagerserver.h>
 #include <gio/gactiongroupexporter.h>
 #include <gio/gdbusactiongroup.h>
+#include <gio/gmenumodel.h>
+#include <gio/gmenu.h>
+#include <gio/gmenumarkup.h>
 
 #undef __GIO_GIO_H_INSIDE__
 
diff --git a/gio/gmenu.c b/gio/gmenu.c
new file mode 100644
index 0000000..c7e5df7
--- /dev/null
+++ b/gio/gmenu.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gmenu.h"
+
+#include <string.h>
+
+struct _GMenuItem
+{
+  GObject parent_instance;
+
+  GHashTable *attributes;
+  GHashTable *links;
+  gboolean    cow;
+};
+
+typedef GObjectClass GMenuItemClass;
+
+G_DEFINE_TYPE (GMenu, g_menu, G_TYPE_MENU_MODEL)
+G_DEFINE_TYPE (GMenuItem, g_menu_item, G_TYPE_OBJECT)
+
+struct item
+{
+  GHashTable *attributes;
+  GHashTable *links;
+};
+
+struct _GMenuPrivate
+{
+  GArray   *items;
+  gboolean  mutable;
+};
+
+static gboolean
+g_menu_is_mutable (GMenuModel *model)
+{
+  GMenu *menu = G_MENU (model);
+
+  return menu->priv->mutable;
+}
+
+static gint
+g_menu_get_n_items (GMenuModel *model)
+{
+  GMenu *menu = G_MENU (model);
+
+  return menu->priv->items->len;
+}
+
+static void
+g_menu_get_item_attributes (GMenuModel  *model,
+                            gint         index_,
+                            GHashTable **quark_table,
+                            GHashTable **string_table,
+                            GVariant   **dictionary)
+{
+  GMenu *menu = G_MENU (model);
+
+  *quark_table = g_array_index (menu->priv->items, struct item, index_).attributes;
+}
+
+static void
+g_menu_get_item_links (GMenuModel  *model,
+                       gint         index_,
+                       GHashTable **quark_table,
+                       GHashTable **string_table)
+{
+  GMenu *menu = G_MENU (model);
+
+  *quark_table = g_array_index (menu->priv->items, struct item, index_).links;
+}
+
+void
+g_menu_insert_item (GMenu     *menu,
+                    gint       index_,
+                    GMenuItem *item)
+{
+  struct item new_item;
+
+  g_return_if_fail (G_IS_MENU (menu));
+  g_return_if_fail (G_IS_MENU_ITEM (item));
+
+  if (index_ < 0 || index_ > menu->priv->items->len)
+    index_ = menu->priv->items->len;
+
+
+  new_item.attributes = g_hash_table_ref (item->attributes);
+  new_item.links = g_hash_table_ref (item->links);
+  item->cow = TRUE;
+
+  g_array_insert_val (menu->priv->items, index_, new_item);
+  g_menu_model_items_changed (G_MENU_MODEL (menu), index_, 0, 1);
+}
+
+void
+g_menu_prepend_item (GMenu     *menu,
+                     GMenuItem *item)
+{
+  g_menu_insert_item (menu, 0, item);
+}
+
+void
+g_menu_append_item (GMenu     *menu,
+                    GMenuItem *item)
+{
+  g_menu_insert_item (menu, -1, item);
+}
+
+void
+g_menu_freeze (GMenu *menu)
+{
+  g_return_if_fail (G_IS_MENU (menu));
+
+  menu->priv->mutable = FALSE;
+}
+
+GMenu *
+g_menu_new (void)
+{
+  return g_object_new (G_TYPE_MENU, NULL);
+}
+
+void
+g_menu_insert (GMenu       *menu,
+               gint         index_,
+               const gchar *label,
+               const gchar *detailed_action)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new (label, detailed_action);
+  g_menu_insert_item (menu, index_, menu_item);
+  g_object_unref (menu_item);
+}
+
+
+void
+g_menu_prepend (GMenu       *menu,
+                const gchar *label,
+                const gchar *detailed_action)
+{
+  g_menu_insert (menu, 0, label, detailed_action);
+}
+
+void
+g_menu_append (GMenu       *menu,
+               const gchar *label,
+               const gchar *detailed_action)
+{
+  g_menu_insert (menu, -1, label, detailed_action);
+}
+
+
+void
+g_menu_insert_section (GMenu       *menu,
+                       gint         index_,
+                       const gchar *label,
+                       GMenuModel  *section)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new_section (label, section);
+  g_menu_insert_item (menu, index_, menu_item);
+  g_object_unref (menu_item);
+}
+
+
+void
+g_menu_prepend_section (GMenu       *menu,
+                        const gchar *label,
+                        GMenuModel  *section)
+{
+  g_menu_insert_section (menu, 0, label, section);
+}
+
+void
+g_menu_append_section (GMenu       *menu,
+                       const gchar *label,
+                       GMenuModel  *section)
+{
+  g_menu_insert_section (menu, -1, label, section);
+}
+
+void
+g_menu_insert_submenu (GMenu       *menu,
+                       gint         index_,
+                       const gchar *label,
+                       GMenuModel  *submenu)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new_submenu (label, submenu);
+  g_menu_insert_item (menu, index_, menu_item);
+  g_object_unref (menu_item);
+}
+
+void
+g_menu_prepend_submenu (GMenu       *menu,
+                        const gchar *label,
+                        GMenuModel  *submenu)
+{
+  g_menu_insert_submenu (menu, 0, label, submenu);
+}
+
+void
+g_menu_append_submenu (GMenu       *menu,
+                       const gchar *label,
+                       GMenuModel  *submenu)
+{
+  g_menu_insert_submenu (menu, -1, label, submenu);
+}
+
+static void
+g_menu_clear_item (struct item *item)
+{
+  if (item->attributes != NULL)
+    g_hash_table_unref (item->attributes);
+  if (item->links != NULL);
+    g_hash_table_unref (item->links);
+}
+
+void
+g_menu_remove (GMenu *menu,
+               gint   index_)
+{
+  g_return_if_fail (G_IS_MENU (menu));
+  g_return_if_fail (0 <= index_ && index_ < menu->priv->items->len);
+
+  g_menu_clear_item (&g_array_index (menu->priv->items, struct item, index_));
+  g_array_remove_index (menu->priv->items, index_);
+  g_menu_model_items_changed (G_MENU_MODEL (menu), index_, 1, 0);
+}
+
+static void
+g_menu_finalize (GObject *object)
+{
+  GMenu *menu = G_MENU (object);
+  struct item *items;
+  gint n_items;
+  gint i;
+
+  n_items = menu->priv->items->len;
+  items = (struct item *) g_array_free (menu->priv->items, FALSE);
+  for (i = 0; i < n_items; i++)
+    g_menu_clear_item (&items[i]);
+  g_free (items);
+
+  G_OBJECT_CLASS (g_menu_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_init (GMenu *menu)
+{
+  menu->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu, G_TYPE_MENU, GMenuPrivate);
+  menu->priv->items = g_array_new (FALSE, FALSE, sizeof (struct item));
+  menu->priv->mutable = TRUE;
+}
+
+static void
+g_menu_class_init (GMenuClass *class)
+{
+  GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_finalize;
+
+  model_class->is_mutable = g_menu_is_mutable;
+  model_class->get_n_items = g_menu_get_n_items;
+  model_class->get_item_attributes = g_menu_get_item_attributes;
+  model_class->get_item_links = g_menu_get_item_links;
+
+  g_type_class_add_private (class, sizeof (GMenuPrivate));
+}
+
+
+static void
+g_menu_item_clear_cow (GMenuItem *menu_item)
+{
+  if (menu_item->cow)
+    {
+      GHashTableIter iter;
+      GHashTable *new;
+      gpointer key;
+      gpointer val;
+
+      new = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_variant_unref);
+      g_hash_table_iter_init (&iter, menu_item->attributes);
+      while (g_hash_table_iter_next (&iter, &key, &val))
+        g_hash_table_insert (new, key, g_variant_ref (val));
+      g_hash_table_unref (menu_item->attributes);
+      menu_item->attributes = new;
+
+      new = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
+      g_hash_table_iter_init (&iter, menu_item->links);
+      while (g_hash_table_iter_next (&iter, &key, &val))
+        g_hash_table_insert (new, key, g_object_ref (val));
+      g_hash_table_unref (menu_item->links);
+      menu_item->links = new;
+
+      menu_item->cow = FALSE;
+    }
+}
+
+static void
+g_menu_item_finalize (GObject *object)
+{
+  GMenuItem *menu_item = G_MENU_ITEM (object);
+
+  g_hash_table_unref (menu_item->attributes);
+  g_hash_table_unref (menu_item->links);
+
+  G_OBJECT_CLASS (g_menu_item_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_item_init (GMenuItem *menu_item)
+{
+  menu_item->attributes = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_variant_unref);
+  menu_item->links = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
+  menu_item->cow = FALSE;
+}
+
+static void
+g_menu_item_class_init (GMenuItemClass *class)
+{
+  class->finalize = g_menu_item_finalize;
+}
+
+GVariant *
+g_menu_item_get_attribute_value (GMenuItem          *menu_item,
+                                 GQuark              attribute_quark,
+                                 const GVariantType *expected_type)
+{
+  GVariant *value = NULL;
+
+  g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
+
+  if (menu_item->attributes)
+    {
+      value = g_hash_table_lookup (menu_item->attributes, GINT_TO_POINTER (attribute_quark));
+
+      if (value != NULL)
+        {
+          if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
+            value = g_variant_ref (value);
+          else
+            value = NULL;
+        }
+    }
+
+  return value;
+}
+
+gboolean
+g_menu_item_get_attribute (GMenuItem   *menu_item,
+                           GQuark       attribute_quark,
+                           const gchar *format_string,
+                           ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_item_get_attribute_value (menu_item, attribute_quark, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+GVariant *
+g_menu_item_get_attribute_value_by_name (GMenuItem          *menu_item,
+                                         const gchar        *attribute_name,
+                                         const GVariantType *expected_type)
+{
+  return g_menu_item_get_attribute_value (menu_item, g_quark_try_string (attribute_name), expected_type);
+}
+
+gboolean
+g_menu_item_get_attribute_by_name (GMenuItem   *menu_item,
+                                   const gchar *attribute_name,
+                                   const gchar *format_string,
+                                   ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_item_get_attribute_value_by_name (menu_item, attribute_name, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+void
+g_menu_item_set_attribute_value (GMenuItem *menu_item,
+                                 GQuark     attribute_quark,
+                                 GVariant  *value)
+{
+  g_return_if_fail (G_IS_MENU_ITEM (menu_item));
+  g_return_if_fail (attribute_quark != 0);
+  g_return_if_fail (value != NULL);
+
+  g_menu_item_clear_cow (menu_item);
+
+  g_hash_table_insert (menu_item->attributes, GINT_TO_POINTER (attribute_quark), g_variant_ref_sink (value));
+}
+
+void
+g_menu_item_set_attribute (GMenuItem   *menu_item,
+                           GQuark       attribute_quark,
+                           const gchar *format_string,
+                           ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  va_start (ap, format_string);
+  value = g_variant_new_va (format_string, NULL, &ap);
+  va_end (ap);
+
+  g_menu_item_set_attribute_value (menu_item, attribute_quark, value);
+}
+
+void
+g_menu_item_unset_attribute (GMenuItem *menu_item,
+                             GQuark     attribute_quark)
+{
+  g_hash_table_remove (menu_item->attributes, GINT_TO_POINTER (attribute_quark));
+}
+
+void
+g_menu_item_set_attribute_value_by_name (GMenuItem   *menu_item,
+                                         const gchar *attribute_name,
+                                         GVariant    *value)
+{
+  g_menu_item_set_attribute_value (menu_item, g_quark_from_string (attribute_name), value);
+}
+
+void
+g_menu_item_set_attribute_by_name (GMenuItem   *menu_item,
+                                   const gchar *attribute_name,
+                                   const gchar *format_string,
+                                   ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  va_start (ap, format_string);
+  value = g_variant_new_va (format_string, NULL, &ap);
+  va_end (ap);
+
+  g_menu_item_set_attribute_value (menu_item, g_quark_from_string (attribute_name), value);
+}
+
+void
+g_menu_item_unset_attribute_by_name (GMenuItem   *menu_item,
+                                     const gchar *attribute_name)
+{
+  g_hash_table_remove (menu_item->attributes, GINT_TO_POINTER (g_quark_try_string (attribute_name)));
+}
+
+
+GMenuModel *
+g_menu_item_get_link (GMenuItem *menu_item,
+                      GQuark     link_quark)
+{
+  GMenuModel *link = NULL;
+
+  g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
+
+  if (menu_item->links)
+    link = g_hash_table_lookup (menu_item->links, GINT_TO_POINTER (link_quark));
+
+  return link ? g_object_ref (link) : NULL;
+}
+
+GMenuModel *
+g_menu_item_get_link_by_name (GMenuItem   *menu_item,
+                              const gchar *link_name)
+{
+  return g_menu_item_get_link (menu_item, g_quark_try_string (link_name));
+}
+
+void
+g_menu_item_set_link (GMenuItem  *menu_item,
+                      GQuark      link_quark,
+                      GMenuModel *link)
+{
+  g_return_if_fail (G_IS_MENU_ITEM (menu_item));
+  g_return_if_fail (link_quark != 0);
+  g_return_if_fail (link != NULL);
+
+  g_menu_item_clear_cow (menu_item);
+
+  g_hash_table_insert (menu_item->links, GINT_TO_POINTER (link_quark), g_object_ref (link));
+}
+
+void
+g_menu_item_set_link_by_name (GMenuItem   *menu_item,
+                              const gchar *link_name,
+                              GMenuModel  *link)
+{
+  g_menu_item_set_link (menu_item, g_quark_from_string (link_name), link);
+}
+
+void
+g_menu_item_set_label (GMenuItem   *menu_item,
+                       const gchar *label)
+{
+  g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_LABEL, "s", label);
+}
+
+void
+g_menu_item_set_submenu (GMenuItem  *menu_item,
+                         GMenuModel *submenu)
+{
+  g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
+}
+
+void
+g_menu_item_set_section (GMenuItem  *menu_item,
+                         GMenuModel *section)
+{
+  g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
+}
+
+void
+g_menu_item_set_action (GMenuItem   *menu_item,
+                        const gchar *action)
+{
+  g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ACTION, "s", action);
+  g_menu_item_unset_attribute (menu_item, G_MENU_ATTRIBUTE_TARGET);
+}
+
+void
+g_menu_item_set_target_value (GMenuItem *menu_item,
+                              GVariant  *target)
+{
+  g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_TARGET, "v", target);
+}
+
+void
+g_menu_item_set_target (GMenuItem   *menu_item,
+                        const gchar *format_string,
+                        ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  va_start (ap, format_string);
+  value = g_variant_new_va (format_string, NULL, &ap);
+  va_end (ap);
+
+  g_menu_item_set_target_value (menu_item, value);
+}
+
+void
+g_menu_item_set_action_and_target_value (GMenuItem   *menu_item,
+                                         const gchar *action,
+                                         GVariant    *target_value)
+{
+  g_menu_item_set_action (menu_item, action);
+  g_menu_item_set_target_value (menu_item, target_value);
+}
+
+void
+g_menu_item_set_action_and_target (GMenuItem   *menu_item,
+                                   const gchar *action,
+                                   const gchar *format_string,
+                                   ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  va_start (ap, format_string);
+  value = g_variant_new_va (format_string, NULL, &ap);
+  va_end (ap);
+
+  g_menu_item_set_action_and_target_value (menu_item, action, value);
+}
+
+void
+g_menu_item_set_detailed_action (GMenuItem   *menu_item,
+                                 const gchar *detailed_action)
+{
+  const gchar *sep;
+
+  sep = strstr (detailed_action, "::");
+
+  if (sep != NULL)
+    {
+      gchar *action;
+
+      action = g_strndup (detailed_action, sep - detailed_action);
+      g_menu_item_set_action_and_target (menu_item, action, "s", sep + 2);
+      g_free (action);
+    }
+
+  else
+    g_menu_item_set_action_and_target_value (menu_item, detailed_action, NULL);
+}
+
+GMenuItem *
+g_menu_item_new (const gchar *label,
+                 const gchar *detailed_action)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
+
+  if (label != NULL)
+    g_menu_item_set_label (menu_item, label);
+
+  if (detailed_action != NULL)
+    g_menu_item_set_detailed_action (menu_item, detailed_action);
+
+  return menu_item;
+}
+
+GMenuItem *
+g_menu_item_new_submenu (const gchar *label,
+                         GMenuModel  *submenu)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
+
+  if (label != NULL)
+    g_menu_item_set_label (menu_item, label);
+
+  g_menu_item_set_submenu (menu_item, submenu);
+
+  return menu_item;
+}
+
+GMenuItem *
+g_menu_item_new_section (const gchar *label,
+                         GMenuModel  *section)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
+
+  if (label != NULL)
+    g_menu_item_set_label (menu_item, label);
+
+  g_menu_item_set_section (menu_item, section);
+
+  return menu_item;
+}
diff --git a/gio/gmenu.h b/gio/gmenu.h
new file mode 100644
index 0000000..1f54d79
--- /dev/null
+++ b/gio/gmenu.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __G_MENU_H__
+#define __G_MENU_H__
+
+#include <gio/gmenumodel.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MENU                                         (g_menu_get_type ())
+#define G_MENU(inst)                                        (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MENU, GMenu))
+#define G_MENU_CLASS(class)                                 (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MENU, GMenuClass))
+#define G_IS_MENU(inst)                                     (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU))
+#define G_IS_MENU_CLASS(class)                              (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MENU))
+#define G_MENU_GET_CLASS(inst)                              (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MENU, GMenuClass))
+
+#define G_TYPE_MENU_ITEM                                    (g_menu_item_get_type ())
+#define G_MENU_ITEM(inst)                                   (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MENU_ITEM, GMenuItem))
+#define G_MENU_ITEM_CLASS(class)                            (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MENU_ITEM, GMenuItemClass))
+#define G_IS_MENU_ITEM(inst)                                (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU_ITEM))
+#define G_IS_MENU_ITEM_CLASS(class)                         (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MENU_ITEM))
+#define G_MENU_ITEM_GET_CLASS(inst)                         (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MENU_ITEM, GMenuItemClass))
+
+typedef struct _GMenuItem                                   GMenuItem;
+
+typedef struct _GMenuPrivate                                GMenuPrivate;
+typedef struct _GMenuClass                                  GMenuClass;
+typedef struct _GMenu                                       GMenu;
+
+struct _GMenu
+{
+  GMenuModel    parent_instance;
+  GMenuPrivate *priv;
+};
+
+struct _GMenuClass
+{
+  GMenuModelClass parent_class;
+};
+
+GType                   g_menu_get_type                                 (void) G_GNUC_CONST;
+GMenu *                 g_menu_new                                      (void);
+
+void                    g_menu_freeze                                   (GMenu              *menu);
+
+void                    g_menu_insert_item                              (GMenu              *menu,
+                                                                         gint                index,
+                                                                         GMenuItem          *item);
+void                    g_menu_prepend_item                             (GMenu              *menu,
+                                                                         GMenuItem          *item);
+void                    g_menu_append_item                              (GMenu              *menu,
+                                                                         GMenuItem          *item);
+void                    g_menu_remove                                   (GMenu              *menu,
+                                                                         gint                index);
+
+void                    g_menu_insert                                   (GMenu              *menu,
+                                                                         gint                index,
+                                                                         const gchar        *label,
+                                                                         const gchar        *detailed_action);
+void                    g_menu_prepend                                  (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         const gchar        *detailed_action);
+void                    g_menu_append                                   (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         const gchar        *detailed_action);
+
+void                    g_menu_insert_section                           (GMenu              *menu,
+                                                                         gint                index,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *section);
+void                    g_menu_prepend_section                          (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *section);
+void                    g_menu_append_section                           (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *section);
+
+void                    g_menu_insert_submenu                           (GMenu              *menu,
+                                                                         gint                index,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *submenu);
+void                    g_menu_prepend_submenu                          (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *submenu);
+void                    g_menu_append_submenu                           (GMenu              *menu,
+                                                                         const gchar        *label,
+                                                                         GMenuModel         *submenu);
+
+
+GType                   g_menu_item_get_type                            (void) G_GNUC_CONST;
+GMenuItem *             g_menu_item_new                                 (const gchar        *label,
+                                                                         const gchar        *detailed_action);
+
+GMenuItem *             g_menu_item_new_submenu                         (const gchar        *label,
+                                                                         GMenuModel         *submenu);
+
+GMenuItem *             g_menu_item_new_section                         (const gchar        *label,
+                                                                         GMenuModel         *section);
+
+GVariant *              g_menu_item_get_attribute_value                 (GMenuItem          *menu_item,
+                                                                         GQuark              attribute_quark,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_item_get_attribute                       (GMenuItem          *menu_item,
+                                                                         GQuark              attribute_quark,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GVariant *              g_menu_item_get_attribute_value_by_name         (GMenuItem          *menu_item,
+                                                                         const gchar        *attribute_name,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_item_get_attribute_by_name               (GMenuItem          *menu_item,
+                                                                         const gchar        *attribute_name,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+void                    g_menu_item_set_attribute_value                 (GMenuItem          *menu_item,
+                                                                         GQuark              attribute_quark,
+                                                                         GVariant           *value);
+void                    g_menu_item_set_attribute                       (GMenuItem          *menu_item,
+                                                                         GQuark              attribute_quark,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+void                    g_menu_item_set_attribute_value_by_name         (GMenuItem          *menu_item,
+                                                                         const gchar        *attribute_name,
+                                                                         GVariant           *value);
+void                    g_menu_item_set_attribute_by_name               (GMenuItem          *menu_item,
+                                                                         const gchar        *attribute_name,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GMenuModel *            g_menu_item_get_link                            (GMenuItem          *menu_item,
+                                                                         GQuark              link_quark);
+GMenuModel *            g_menu_item_get_link_by_name                    (GMenuItem          *menu_item,
+                                                                         const gchar        *link_name);
+void                    g_menu_item_set_link                            (GMenuItem          *menu_item,
+                                                                         GQuark              link_quark,
+                                                                         GMenuModel         *link);
+void                    g_menu_item_set_link_by_name                    (GMenuItem          *menu_item,
+                                                                         const gchar        *link_name,
+                                                                         GMenuModel         *link);
+void                    g_menu_item_set_label                           (GMenuItem          *menu_item,
+                                                                         const gchar        *label);
+void                    g_menu_item_set_submenu                         (GMenuItem          *menu_item,
+                                                                         GMenuModel         *submenu);
+void                    g_menu_item_set_section                         (GMenuItem          *menu_item,
+                                                                         GMenuModel         *section);
+void                    g_menu_item_set_action                          (GMenuItem          *menu_item,
+                                                                         const gchar        *action);
+void                    g_menu_item_set_target_value                    (GMenuItem          *menu_item,
+                                                                         GVariant           *target);
+void                    g_menu_item_set_target                          (GMenuItem          *menu_item,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+void                    g_menu_item_set_action_and_target_value         (GMenuItem          *menu_item,
+                                                                         const gchar        *action,
+                                                                         GVariant           *target_value);
+void                    g_menu_item_set_action_and_target               (GMenuItem          *menu_item,
+                                                                         const gchar        *action,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+void                    g_menu_item_set_detailed_action                 (GMenuItem          *menu_item,
+                                                                         const gchar        *detailed_action);
+G_END_DECLS
+
+#endif /* __G_MENU_H__ */
diff --git a/gio/gmenumarkup.c b/gio/gmenumarkup.c
new file mode 100644
index 0000000..38cca8e
--- /dev/null
+++ b/gio/gmenumarkup.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gmenumarkup.h"
+
+#include <gi18n.h>
+
+struct frame
+{
+  GMenu        *menu;
+  GMenuItem    *item;
+  struct frame *prev;
+};
+
+typedef struct
+{
+  GHashTable *objects;
+  struct frame frame;
+
+  /* attributes */
+  GVariantType *type;
+  GString      *string;
+  gchar        *name;
+} GMenuMarkupState;
+
+static void
+g_menu_markup_push_frame (GMenuMarkupState *state,
+                          GMenu            *menu,
+                          GMenuItem        *item)
+{
+  struct frame *new;
+
+  new = g_slice_new (struct frame);
+  *new = state->frame;
+
+  state->frame.menu = menu;
+  state->frame.item = item;
+  state->frame.prev = new;
+}
+
+static void
+g_menu_markup_pop_frame (GMenuMarkupState *state)
+{
+  struct frame *prev = state->frame.prev;
+
+  if (state->frame.item)
+    {
+      g_assert (prev->menu != NULL);
+      g_menu_append_item (prev->menu, state->frame.item);
+    }
+
+  state->frame = *prev;
+
+  g_slice_free (struct frame, prev);
+}
+
+static void
+add_string_attributes (GMenuItem    *item,
+                       const gchar **names,
+                       const gchar **values)
+{
+  gint i;
+
+  for (i = 0; names[i]; i++)
+    {
+      GQuark attribute = g_quark_from_string (names[i]);
+      g_menu_item_set_attribute (item, attribute, "s", values[i]);
+    }
+}
+
+static void
+g_menu_markup_start_element (GMarkupParseContext  *context,
+                             const gchar          *element_name,
+                             const gchar         **attribute_names,
+                             const gchar         **attribute_values,
+                             gpointer              user_data,
+                             GError              **error)
+{
+  GMenuMarkupState *state = user_data;
+
+#define COLLECT(first, ...) \
+  g_markup_collect_attributes (element_name,                                 \
+                               attribute_names, attribute_values, error,     \
+                               first, __VA_ARGS__, G_MARKUP_COLLECT_INVALID)
+#define OPTIONAL   G_MARKUP_COLLECT_OPTIONAL
+#define STRDUP     G_MARKUP_COLLECT_STRDUP
+#define STRING     G_MARKUP_COLLECT_STRING
+#define NO_ATTRS() COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
+
+  if (!(state->frame.menu || state->frame.menu || state->string))
+    {
+      /* Can only have <menu> here. */
+      if (g_str_equal (element_name, "menu"))
+        {
+          gchar *id;
+
+          if (COLLECT (STRDUP, "id", &id))
+            {
+              GMenu *menu;
+
+              menu = g_menu_new ();
+              g_hash_table_insert (state->objects, id, menu);
+              g_menu_markup_push_frame (state, menu, NULL);
+            }
+
+          return;
+        }
+    }
+
+  if (state->frame.menu)
+    {
+      /* Can have '<item>', '<submenu>' or '<section>' here. */
+      if (g_str_equal (element_name, "item"))
+        {
+          GMenuItem *item;
+
+          item = g_menu_item_new (NULL, NULL);
+          add_string_attributes (item, attribute_names, attribute_values);
+          g_menu_markup_push_frame (state, NULL, item);
+          return;
+        }
+
+      else if (g_str_equal (element_name, "submenu"))
+        {
+          GMenuItem *item;
+          GMenu *menu;
+
+          menu = g_menu_new ();
+          item = g_menu_item_new_submenu (NULL, G_MENU_MODEL (menu));
+          add_string_attributes (item, attribute_names, attribute_values);
+          g_menu_markup_push_frame (state, menu, item);
+          return;
+        }
+
+      else if (g_str_equal (element_name, "section"))
+        {
+          GMenuItem *item;
+          GMenu *menu;
+
+          menu = g_menu_new ();
+          item = g_menu_item_new_section (NULL, G_MENU_MODEL (menu));
+          add_string_attributes (item, attribute_names, attribute_values);
+          g_menu_markup_push_frame (state, menu, item);
+          return;
+        }
+    }
+
+  if (state->frame.item)
+    {
+      /* Can have '<attribute>' or '<link>' here. */
+      if (g_str_equal (element_name, "attribute"))
+        {
+          const gchar *typestr;
+          const gchar *name;
+
+          if (COLLECT (STRING,            "name", &name,
+                       OPTIONAL | STRING, "type", &typestr))
+            {
+              if (typestr && !g_variant_type_string_is_valid (typestr))
+                {
+                  g_set_error (error, G_VARIANT_PARSE_ERROR,
+                               G_VARIANT_PARSE_ERROR_INVALID_TYPE_STRING,
+                               "Invalid GVariant type string '%s'", typestr);
+                  return;
+                }
+
+              state->type = typestr ? g_variant_type_new (typestr) : NULL;
+              state->string = g_string_new (NULL);
+              state->name = g_strdup (name);
+              g_menu_markup_push_frame (state, NULL, NULL);
+            }
+
+          return;
+        }
+
+      if (g_str_equal (element_name, "link"))
+        {
+          const gchar *name;
+          const gchar *id;
+
+          if (COLLECT (STRING,            "name", &name,
+                       STRING | OPTIONAL, "id",   &id))
+            {
+              GMenu *menu;
+
+              menu = g_menu_new ();
+              g_menu_item_set_link_by_name (state->frame.item, name, G_MENU_MODEL (menu));
+              g_menu_markup_push_frame (state, menu, NULL);
+
+              if (id != NULL)
+                g_hash_table_insert (state->objects, g_strdup (id), g_object_ref (menu));
+            }
+
+          return;
+        }
+    }
+
+  {
+    const GSList *element_stack;
+
+    element_stack = g_markup_parse_context_get_element_stack (context);
+
+    if (element_stack->next)
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   _("Element <%s> not allowed inside <%s>"),
+                   element_name, (const gchar *) element_stack->next->data);
+
+    else
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   _("Element <%s> not allowed at toplevel"), element_name);
+  }
+}
+
+static void
+g_menu_markup_end_element (GMarkupParseContext  *context,
+                           const gchar          *element_name,
+                           gpointer              user_data,
+                           GError              **error)
+{
+  GMenuMarkupState *state = user_data;
+
+  g_menu_markup_pop_frame (state);
+
+  if (state->string)
+    {
+      GVariant *value;
+      gchar *text;
+
+      text = g_string_free (state->string, FALSE);
+      state->string = NULL;
+
+      /* If error is set here, it will follow us out, ending the parse.
+       * We still need to free everything, though.
+       */
+      if ((value = g_variant_parse (state->type, text, NULL, NULL, error)))
+        {
+          g_menu_item_set_attribute_value_by_name (state->frame.item, state->name, value);
+          g_variant_unref (value);
+        }
+
+      g_free (state->name);
+      state->name = NULL;
+
+      if (state->type)
+        {
+          g_variant_type_free (state->type);
+          state->type = NULL;
+        }
+
+      g_free (text);
+    }
+}
+
+static void
+g_menu_markup_text (GMarkupParseContext  *context,
+                    const gchar          *text,
+                    gsize                 text_len,
+                    gpointer              user_data,
+                    GError              **error)
+{
+  GMenuMarkupState *state = user_data;
+  gint i;
+
+  for (i = 0; i < text_len; i++)
+    if (!g_ascii_isspace (text[i]))
+      {
+        if (state->string)
+          g_string_append_len (state->string, text, text_len);
+
+        else
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       _("text may not appear inside <%s>"),
+                       g_markup_parse_context_get_element (context));
+        break;
+      }
+}
+
+static void
+g_menu_markup_error (GMarkupParseContext *context,
+                     GError              *error,
+                     gpointer             user_data)
+{
+  GMenuMarkupState *state = user_data;
+
+  while (state->frame.prev)
+    {
+      struct frame *prev = state->frame.prev;
+
+      state->frame = *prev;
+
+      g_slice_free (struct frame, prev);
+    }
+
+  if (state->string)
+    g_string_free (state->string, TRUE);
+
+  if (state->type)
+    g_variant_type_free (state->type);
+
+  if (state->name)
+    g_free (state->name);
+
+  if (state->objects)
+    g_hash_table_unref (state->objects);
+
+  g_slice_free (GMenuMarkupState, state);
+}
+
+static GMarkupParser g_menu_subparser =
+{
+  g_menu_markup_start_element,
+  g_menu_markup_end_element,
+  g_menu_markup_text,
+  NULL,                            /* passthrough */
+  g_menu_markup_error
+};
+
+void
+g_menu_markup_parser_start (GMarkupParseContext *context,
+                            GHashTable          *objects)
+{
+  GMenuMarkupState *state;
+
+  g_return_if_fail (context != NULL);
+
+  state = g_slice_new0 (GMenuMarkupState);
+
+  if (objects != NULL)
+    state->objects = g_hash_table_ref (objects);
+  else
+    state->objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+  g_markup_parse_context_push (context, &g_menu_subparser, state);
+}
+
+GHashTable *
+g_menu_markup_parser_end (GMarkupParseContext *context)
+{
+  GMenuMarkupState *state = g_markup_parse_context_pop (context);
+  GHashTable *objects;
+
+  objects = state->objects;
+  g_slice_free (GMenuMarkupState, state);
+
+  return objects;
+}
+
+void
+g_menu_markup_parser_start_menu (GMarkupParseContext *context,
+                                 GHashTable          *objects)
+{
+  GMenuMarkupState *state;
+
+  g_return_if_fail (context != NULL);
+
+  state = g_slice_new0 (GMenuMarkupState);
+
+  if (objects)
+    state->objects = g_hash_table_ref (objects);
+
+  g_markup_parse_context_push (context, &g_menu_subparser, state);
+
+  state->frame.menu = g_menu_new ();
+}
+
+GMenu *
+g_menu_markup_parser_end_menu (GMarkupParseContext *context)
+{
+  GMenuMarkupState *state = g_markup_parse_context_pop (context);
+  GMenu *menu;
+
+  menu = state->frame.menu;
+
+  g_hash_table_unref (state->objects);
+  g_slice_free (GMenuMarkupState, state);
+
+  return menu;
+}
+
+static void
+indent_string (GString *string,
+               gint     indent)
+{
+  while (indent--)
+    g_string_append_c (string, ' ');
+}
+
+GString *
+g_menu_markup_print_string (GString    *string,
+                            GMenuModel *model,
+                            gint        indent,
+                            gint        tabstop)
+{
+  gint i, n;
+
+  if G_UNLIKELY (string == NULL)
+    string = g_string_new (NULL);
+
+  n = g_menu_model_get_n_items (model);
+
+  for (i = 0; i < n; i++)
+    {
+      GMenuAttributeIter *attr_iter;
+      GMenuLinkIter *link_iter;
+      GMenuModelItem item;
+      gboolean need_nl;
+
+      if (i > 0)
+        g_string_append (string, "\n");
+
+      g_menu_model_get_item (model, i, &item);
+
+      indent_string (string, indent);
+      g_string_append (string, "<item>\n");
+
+      attr_iter = g_menu_model_item_iterate_attributes (&item);
+      link_iter = g_menu_model_item_iterate_links (&item);
+      need_nl = FALSE;
+
+      while (g_menu_attribute_iter_next (attr_iter))
+        {
+          const gchar *name = g_menu_attribute_iter_get_name (attr_iter);
+          GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
+          gchar *printed;
+          gchar *str;
+
+          printed = g_variant_print (value, TRUE);
+          str = g_markup_printf_escaped ("<attribute name='%s'>%s</attribute>\n", name, printed);
+          indent_string (string, indent + tabstop);
+          g_string_append (string, str);
+          g_variant_unref (value);
+          g_free (printed);
+          g_free (str);
+
+          need_nl = TRUE;
+        }
+      g_object_unref (attr_iter);
+
+      while (g_menu_link_iter_next (link_iter))
+        {
+          const gchar *name = g_menu_link_iter_get_name (link_iter);
+          GMenuModel *menu = g_menu_link_iter_get_value (link_iter);
+          gchar *str;
+
+          if (need_nl)
+            g_string_append_c (string, '\n');
+
+          str = g_markup_printf_escaped ("<link name='%s'>\n", name);
+          indent_string (string, indent + tabstop);
+          g_string_append (string, str);
+          g_free (str);
+
+          g_menu_markup_print_string (string, menu, indent + 2 * tabstop, tabstop);
+
+          indent_string (string, indent + tabstop);
+          g_string_append (string, "</link>\n");
+
+          need_nl = TRUE;
+        }
+      g_object_unref (link_iter);
+
+      indent_string (string, indent);
+      g_string_append (string, "</item>\n");
+    }
+
+  return string;
+}
+
+void
+g_menu_markup_print_stderr (GMenuModel *model)
+{
+  GString *string;
+
+  string = g_string_new ("<menu>\n");
+  g_menu_markup_print_string (string, model, 2, 2);
+  g_printerr ("%s</menu>\n", string->str);
+  g_string_free (string, TRUE);
+}
diff --git a/gio/gmenumarkup.h b/gio/gmenumarkup.h
new file mode 100644
index 0000000..375e653
--- /dev/null
+++ b/gio/gmenumarkup.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __G_MENU_MARKUP_H__
+#define __G_MENU_MARKUP_H__
+
+#include <gio/gmenu.h>
+
+void                    g_menu_markup_parser_start                      (GMarkupParseContext  *context,
+                                                                         GHashTable           *objects);
+GHashTable *            g_menu_markup_parser_end                        (GMarkupParseContext  *context);
+
+void                    g_menu_markup_parser_start_menu                 (GMarkupParseContext  *context,
+                                                                         GHashTable           *objects);
+GMenu *                 g_menu_markup_parser_end_menu                   (GMarkupParseContext  *context);
+
+void                    g_menu_markup_print_stderr                      (GMenuModel           *model);
+
+#endif /* __G_MENU_MARKUP_H__ */
diff --git a/gio/gmenumodel.c b/gio/gmenumodel.c
new file mode 100644
index 0000000..a34ff3a
--- /dev/null
+++ b/gio/gmenumodel.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gmenumodel.h"
+
+
+typedef struct
+{
+  GMenuLinkIter parent_instance;
+  GHashTableIter iter;
+  GHashTable *table;
+  gboolean is_quarks;
+} GMenuLinkHashIter;
+
+typedef GMenuLinkIterClass GMenuLinkHashIterClass;
+
+G_DEFINE_TYPE (GMenuLinkHashIter, g_menu_link_hash_iter, G_TYPE_MENU_LINK_ITER)
+
+static gboolean
+g_menu_link_hash_iter_get_next (GMenuLinkIter  *link_iter,
+                                const gchar   **name,
+                                GMenuModel    **value)
+{
+  GMenuLinkHashIter *iter = (GMenuLinkHashIter *) link_iter;
+  gpointer keyptr, valueptr;
+
+  if (!g_hash_table_iter_next (&iter->iter, &keyptr, &valueptr))
+    return FALSE;
+
+  if (iter->is_quarks)
+    *name = g_quark_to_string (GPOINTER_TO_INT (keyptr));
+  else
+    *name = keyptr;
+
+  *value = g_object_ref (valueptr);
+
+  return TRUE;
+}
+
+static void
+g_menu_link_hash_iter_finalize (GObject *object)
+{
+  GMenuLinkHashIter *iter = (GMenuLinkHashIter *) object;
+
+  g_hash_table_unref (iter->table);
+
+  G_OBJECT_CLASS (g_menu_link_hash_iter_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_link_hash_iter_init (GMenuLinkHashIter *iter)
+{
+}
+
+static void
+g_menu_link_hash_iter_class_init (GMenuLinkHashIterClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_link_hash_iter_finalize;
+  class->get_next = g_menu_link_hash_iter_get_next;
+}
+
+
+typedef struct
+{
+  GMenuAttributeIter parent_instance;
+  GHashTableIter iter;
+  GHashTable *table;
+  gboolean is_quarks;
+} GMenuAttributeHashIter;
+
+typedef GMenuAttributeIterClass GMenuAttributeHashIterClass;
+
+G_DEFINE_TYPE (GMenuAttributeHashIter, g_menu_attribute_hash_iter, G_TYPE_MENU_ATTRIBUTE_ITER)
+
+static gboolean
+g_menu_attribute_hash_iter_get_next (GMenuAttributeIter  *attr_iter,
+                                     const gchar        **name,
+                                     GVariant           **value)
+{
+  GMenuAttributeHashIter *iter = (GMenuAttributeHashIter *) attr_iter;
+  gpointer keyptr, valueptr;
+
+  if (!g_hash_table_iter_next (&iter->iter, &keyptr, &valueptr))
+    return FALSE;
+
+  if (iter->is_quarks)
+    *name = g_quark_to_string (GPOINTER_TO_INT (keyptr));
+  else
+    *name = keyptr;
+
+  *value = g_variant_ref (valueptr);
+
+  return TRUE;
+}
+
+static void
+g_menu_attribute_hash_iter_finalize (GObject *object)
+{
+  GMenuAttributeHashIter *iter = (GMenuAttributeHashIter *) object;
+
+  g_hash_table_unref (iter->table);
+
+  G_OBJECT_CLASS (g_menu_attribute_hash_iter_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_attribute_hash_iter_init (GMenuAttributeHashIter *iter)
+{
+}
+
+static void
+g_menu_attribute_hash_iter_class_init (GMenuAttributeHashIterClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_attribute_hash_iter_finalize;
+  class->get_next = g_menu_attribute_hash_iter_get_next;
+}
+
+
+typedef struct
+{
+  GMenuAttributeIter parent_instance;
+  GVariantIter iter;
+  GVariant *dict;
+} GMenuAttributeDictIter;
+
+typedef GMenuAttributeIterClass GMenuAttributeDictIterClass;
+
+G_DEFINE_TYPE (GMenuAttributeDictIter, g_menu_attribute_dict_iter, G_TYPE_MENU_ATTRIBUTE_ITER)
+
+static gboolean
+g_menu_attribute_dict_iter_get_next (GMenuAttributeIter  *attr_iter,
+                                     const gchar        **name,
+                                     GVariant           **value)
+{
+  GMenuAttributeDictIter *iter = (GMenuAttributeDictIter *) attr_iter;
+
+  return g_variant_iter_next (&iter->iter, "{&sv}", name, value);
+}
+
+static void
+g_menu_attribute_dict_iter_finalize (GObject *object)
+{
+  GMenuAttributeDictIter *iter = (GMenuAttributeDictIter *) object;
+
+  g_variant_unref (iter->dict);
+
+  G_OBJECT_CLASS (g_menu_attribute_dict_iter_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_attribute_dict_iter_init (GMenuAttributeDictIter *iter)
+{
+}
+
+static void
+g_menu_attribute_dict_iter_class_init (GMenuAttributeDictIterClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_attribute_dict_iter_finalize;
+  class->get_next = g_menu_attribute_dict_iter_get_next;
+}
+
+
+G_DEFINE_ABSTRACT_TYPE (GMenuModel, g_menu_model, G_TYPE_OBJECT)
+
+
+guint g_menu_model_items_changed_signal;
+
+#define MAKE_QUARK(str) \
+  GQuark                                                \
+  g_menu_model_get_ ## str ## _quark (void)             \
+  {                                                     \
+    static GQuark quark;                                \
+    if G_UNLIKELY (quark == 0)                          \
+      quark = g_quark_from_static_string (#str);        \
+    return quark;                                       \
+  }
+
+MAKE_QUARK(action)
+MAKE_QUARK(target)
+MAKE_QUARK(label)
+MAKE_QUARK(submenu)
+MAKE_QUARK(section)
+
+static GMenuAttributeIter *
+g_menu_model_real_iterate_item_attributes (GMenuModel *model,
+                                           gint        item_index)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GVariant *dictionary = NULL;
+  GMenuAttributeIter *result;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_attributes (model, item_index, &quark_table, &string_table, &dictionary);
+
+  if (quark_table)
+    {
+      GMenuAttributeHashIter *iter = g_object_new (g_menu_attribute_hash_iter_get_type (), NULL);
+      g_hash_table_iter_init (&iter->iter, quark_table);
+      iter->table = g_hash_table_ref (quark_table);
+      iter->is_quarks = TRUE;
+      result = G_MENU_ATTRIBUTE_ITER (iter);
+    }
+
+  else if (string_table)
+    {
+      GMenuAttributeHashIter *iter = g_object_new (g_menu_attribute_hash_iter_get_type (), NULL);
+      g_hash_table_iter_init (&iter->iter, string_table);
+      iter->table = g_hash_table_ref (string_table);
+      iter->is_quarks = FALSE;
+      result = G_MENU_ATTRIBUTE_ITER (iter);
+    }
+
+  else if (dictionary)
+    {
+      GMenuAttributeDictIter *iter = g_object_new (g_menu_attribute_dict_iter_get_type (), NULL);
+      g_variant_iter_init (&iter->iter, dictionary);
+      iter->dict = g_variant_ref (dictionary);
+      result = G_MENU_ATTRIBUTE_ITER (iter);
+    }
+
+  else
+    {
+      g_critical ("GMenuModel implementation '%s' doesn't override iterate_item_attributes() "
+                  "and fails to return sane values from get_item_attributes()",
+                  G_OBJECT_TYPE_NAME (model));
+      result = NULL;
+    }
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+  if (dictionary != NULL)
+    g_variant_unref (dictionary);
+
+  return result;
+}
+
+static GVariant *
+g_menu_model_real_get_item_attribute_value_by_name (GMenuModel         *model,
+                                                    gint                item_index,
+                                                    const gchar        *attribute_name,
+                                                    const GVariantType *expected_type)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GVariant *dictionary = NULL;
+  GVariant *value = NULL;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_attributes (model, item_index, &quark_table, &string_table, &dictionary);
+
+  if (string_table != NULL)
+    {
+      value = g_hash_table_lookup (string_table, attribute_name);
+
+      if (value != NULL)
+        {
+          if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
+            value = g_variant_ref (value);
+          else
+            value = NULL;
+        }
+    }
+
+  else if (quark_table != NULL)
+    {
+      GQuark quark = g_quark_try_string (attribute_name);
+
+      if (quark != 0)
+        value = g_hash_table_lookup (quark_table, GINT_TO_POINTER (quark));
+
+      if (value != NULL)
+        {
+          if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
+            value = g_variant_ref (value);
+          else
+            value = NULL;
+        }
+    }
+
+  else if (dictionary != NULL)
+    value = g_variant_lookup_value (dictionary, attribute_name, expected_type);
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+  if (dictionary != NULL)
+    g_variant_unref (dictionary);
+
+  return value;
+}
+
+static GVariant *
+g_menu_model_real_get_item_attribute_value (GMenuModel         *model,
+                                            gint                item_index,
+                                            GQuark              attribute_quark,
+                                            const GVariantType *expected_type)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GVariant *dictionary = NULL;
+  GVariant *value = NULL;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_attributes (model, item_index, &quark_table, &string_table, &dictionary);
+
+  if (quark_table != NULL)
+    {
+      value = g_hash_table_lookup (quark_table, GINT_TO_POINTER (attribute_quark));
+
+      if (value != NULL)
+        {
+          if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
+            value = g_variant_ref (value);
+          else
+            value = NULL;
+        }
+    }
+
+  else if (string_table != NULL)
+    {
+      value = g_hash_table_lookup (string_table, g_quark_to_string (attribute_quark));
+
+      if (value != NULL)
+        {
+          if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
+            value = g_variant_ref (value);
+          else
+            value = NULL;
+        }
+    }
+
+  else if (dictionary != NULL)
+    value = g_variant_lookup_value (dictionary, g_quark_to_string (attribute_quark), expected_type);
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+  if (dictionary != NULL)
+    g_variant_unref (dictionary);
+
+  return value;
+  return NULL;
+}
+
+static GMenuLinkIter *
+g_menu_model_real_iterate_item_links (GMenuModel *model,
+                                      gint        item_index)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GMenuLinkIter *result;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_links (model, item_index, &quark_table, &string_table);
+
+  if (quark_table)
+    {
+      GMenuLinkHashIter *iter = g_object_new (g_menu_link_hash_iter_get_type (), NULL);
+      g_hash_table_iter_init (&iter->iter, quark_table);
+      iter->table = g_hash_table_ref (quark_table);
+      iter->is_quarks = TRUE;
+      result = G_MENU_LINK_ITER (iter);
+    }
+
+  else if (string_table)
+    {
+      GMenuLinkHashIter *iter = g_object_new (g_menu_link_hash_iter_get_type (), NULL);
+      g_hash_table_iter_init (&iter->iter, string_table);
+      iter->table = g_hash_table_ref (string_table);
+      iter->is_quarks = FALSE;
+      result = G_MENU_LINK_ITER (iter);
+    }
+
+  else
+    {
+      g_critical ("GMenuModel implementation '%s' doesn't override iterate_item_links() "
+                  "and fails to return sane values from get_item_links()",
+                  G_OBJECT_TYPE_NAME (model));
+      result = NULL;
+    }
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+
+  return result;
+}
+
+static GMenuModel *
+g_menu_model_real_get_item_link_by_name (GMenuModel  *model,
+                                         gint         item_index,
+                                         const gchar *link_name)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GMenuModel *link = NULL;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_links (model, item_index, &quark_table, &string_table);
+
+  if (string_table != NULL)
+    link = g_hash_table_lookup (string_table, link_name);
+
+  else if (quark_table != NULL)
+    {
+      GQuark quark = g_quark_try_string (link_name);
+
+      if (quark != 0)
+        link = g_hash_table_lookup (quark_table, GINT_TO_POINTER (quark));
+    }
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+
+  return link ? g_object_ref (link) : NULL;
+}
+
+static GMenuModel *
+g_menu_model_real_get_item_link (GMenuModel *model,
+                                 gint        item_index,
+                                 GQuark      link_quark)
+{
+  GHashTable *string_table = NULL;
+  GHashTable *quark_table = NULL;
+  GMenuModel *link = NULL;
+
+  G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_links (model, item_index, &quark_table, &string_table);
+
+  if (quark_table != NULL)
+    link = g_hash_table_lookup (quark_table, GINT_TO_POINTER (link_quark));
+
+  else if (string_table != NULL)
+    link = g_hash_table_lookup (string_table, g_quark_to_string (link_quark));
+
+  if (string_table != NULL)
+    g_hash_table_unref (string_table);
+  if (quark_table != NULL)
+    g_hash_table_unref (quark_table);
+
+  return link ? g_object_ref (link) : NULL;
+}
+
+static void
+g_menu_model_init (GMenuModel *model)
+{
+}
+
+static void
+g_menu_model_class_init (GMenuModelClass *class)
+{
+  class->iterate_item_attributes = g_menu_model_real_iterate_item_attributes;
+  class->get_item_attribute_value_by_name = g_menu_model_real_get_item_attribute_value_by_name;
+  class->get_item_attribute_value = g_menu_model_real_get_item_attribute_value;
+  class->iterate_item_links = g_menu_model_real_iterate_item_links;
+  class->get_item_link_by_name = g_menu_model_real_get_item_link_by_name;
+  class->get_item_link = g_menu_model_real_get_item_link;
+
+  g_menu_model_items_changed_signal =
+    g_signal_new ("items-changed", G_TYPE_MENU_MODEL,
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+                  g_cclosure_marshal_generic, G_TYPE_NONE,
+                  3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+}
+
+
+
+gboolean
+g_menu_model_is_mutable (GMenuModel *model)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->is_mutable (model);
+}
+
+gint
+g_menu_model_get_n_items (GMenuModel *model)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->get_n_items (model);
+}
+
+void
+g_menu_model_get_item (GMenuModel     *model,
+                       gint            item_index,
+                       GMenuModelItem *item)
+{
+  g_return_if_fail (G_IS_MENU_MODEL (model));
+
+  item->model = model;
+  item->index_ = item_index;
+}
+
+GMenuAttributeIter *
+g_menu_model_iterate_item_attributes (GMenuModel *model,
+                                      gint        item_index)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->iterate_item_attributes (model, item_index);
+}
+
+GVariant *
+g_menu_model_get_item_attribute_value_by_name (GMenuModel         *model,
+                                               gint                item_index,
+                                               const gchar        *attribute_name,
+                                               const GVariantType *expected_type)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_attribute_value_by_name (model, item_index, attribute_name, expected_type);
+}
+
+gboolean
+g_menu_model_get_item_attribute_by_name (GMenuModel  *model,
+                                         gint         item_index,
+                                         const gchar *attribute_name,
+                                         const gchar *format_string,
+                                         ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_model_get_item_attribute_value_by_name (model, item_index, attribute_name, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+GVariant *
+g_menu_model_get_item_attribute_value (GMenuModel         *model,
+                                       gint                item_index,
+                                       GQuark              attribute_quark,
+                                       const GVariantType *expected_type)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_attribute_value (model, item_index, attribute_quark, expected_type);
+}
+
+gboolean
+g_menu_model_get_item_attribute (GMenuModel  *model,
+                                 gint         item_index,
+                                 GQuark       attribute_quark,
+                                 const gchar *format_string,
+                                 ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_model_get_item_attribute_value (model, item_index, attribute_quark, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+GMenuLinkIter *
+g_menu_model_iterate_item_links (GMenuModel *model,
+                                 gint        item_index)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->iterate_item_links (model, item_index);
+}
+
+GMenuModel *
+g_menu_model_get_item_link_by_name (GMenuModel  *model,
+                                    gint         item_index,
+                                    const gchar *link_name)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_link_by_name (model, item_index, link_name);
+}
+
+GMenuModel *
+g_menu_model_get_item_link (GMenuModel *model,
+                            gint        item_index,
+                            GQuark      link_quark)
+{
+  return G_MENU_MODEL_GET_CLASS (model)
+    ->get_item_link (model, item_index, link_quark);
+}
+
+void
+g_menu_model_items_changed (GMenuModel *model,
+                            gint        index_,
+                            gint        removed,
+                            gint        added)
+{
+  g_signal_emit (model, g_menu_model_items_changed_signal, 0, index_, removed, added);
+}
+
+G_DEFINE_ABSTRACT_TYPE (GMenuAttributeIter, g_menu_attribute_iter, G_TYPE_OBJECT)
+
+struct _GMenuAttributeIterPrivate
+{
+  const gchar *name;
+  GVariant *value;
+  gboolean valid;
+};
+
+gboolean
+g_menu_attribute_iter_get_next (GMenuAttributeIter  *iter,
+                                const gchar        **name,
+                                GVariant           **value)
+{
+  if (iter->priv->value)
+    g_variant_unref (iter->priv->value);
+
+  iter->priv->valid = G_MENU_ATTRIBUTE_ITER_GET_CLASS (iter)
+    ->get_next (iter, &iter->priv->name, &iter->priv->value);
+
+  if (name)
+    *name = iter->priv->name;
+
+  if (value)
+    *value = g_variant_ref (iter->priv->value);
+
+  return iter->priv->valid;
+}
+
+gboolean
+g_menu_attribute_iter_next (GMenuAttributeIter *iter)
+{
+  return g_menu_attribute_iter_get_next (iter, NULL, NULL);
+}
+
+const gchar *
+g_menu_attribute_iter_get_name (GMenuAttributeIter *iter)
+{
+  g_return_val_if_fail (iter->priv->valid, NULL);
+
+  return iter->priv->name;
+}
+
+GVariant *
+g_menu_attribute_iter_get_value (GMenuAttributeIter *iter)
+{
+  g_return_val_if_fail (iter->priv->valid, NULL);
+
+  return g_variant_ref (iter->priv->value);
+}
+
+static void
+g_menu_attribute_iter_finalize (GObject *object)
+{
+  GMenuAttributeIter *iter = G_MENU_ATTRIBUTE_ITER (object);
+
+  if (iter->priv->value)
+    g_variant_unref (iter->priv->value);
+
+  G_OBJECT_CLASS (g_menu_attribute_iter_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_attribute_iter_init (GMenuAttributeIter *iter)
+{
+  iter->priv = G_TYPE_INSTANCE_GET_PRIVATE (iter, G_TYPE_MENU_ATTRIBUTE_ITER, GMenuAttributeIterPrivate);
+}
+
+static void
+g_menu_attribute_iter_class_init (GMenuAttributeIterClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_attribute_iter_finalize;
+
+  g_type_class_add_private (class, sizeof (GMenuAttributeIterPrivate));
+}
+
+G_DEFINE_ABSTRACT_TYPE (GMenuLinkIter, g_menu_link_iter, G_TYPE_OBJECT)
+
+struct _GMenuLinkIterPrivate
+{
+  const gchar *name;
+  GMenuModel *value;
+  gboolean valid;
+};
+
+gboolean
+g_menu_link_iter_get_next (GMenuLinkIter  *iter,
+                           const gchar   **name,
+                           GMenuModel    **value)
+{
+  if (iter->priv->value)
+    g_object_unref (iter->priv->value);
+
+  iter->priv->valid = G_MENU_LINK_ITER_GET_CLASS (iter)
+    ->get_next (iter, &iter->priv->name, &iter->priv->value);
+
+  if (name)
+    *name = iter->priv->name;
+
+  if (value)
+    *value = g_object_ref (iter->priv->value);
+
+  return iter->priv->valid;
+}
+
+gboolean
+g_menu_link_iter_next (GMenuLinkIter *iter)
+{
+  return g_menu_link_iter_get_next (iter, NULL, NULL);
+}
+
+const gchar *
+g_menu_link_iter_get_name (GMenuLinkIter *iter)
+{
+  g_return_val_if_fail (iter->priv->valid, NULL);
+
+  return iter->priv->name;
+}
+
+GMenuModel *
+g_menu_link_iter_get_value (GMenuLinkIter *iter)
+{
+  g_return_val_if_fail (iter->priv->valid, NULL);
+
+  return g_object_ref (iter->priv->value);
+}
+
+static void
+g_menu_link_iter_finalize (GObject *object)
+{
+  GMenuLinkIter *iter = G_MENU_LINK_ITER (object);
+
+  if (iter->priv->value)
+    g_object_unref (iter->priv->value);
+
+  G_OBJECT_CLASS (g_menu_link_iter_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_menu_link_iter_init (GMenuLinkIter *iter)
+{
+  iter->priv = G_TYPE_INSTANCE_GET_PRIVATE (iter, G_TYPE_MENU_LINK_ITER, GMenuLinkIterPrivate);
+}
+
+static void
+g_menu_link_iter_class_init (GMenuLinkIterClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_menu_link_iter_finalize;
+
+  g_type_class_add_private (class, sizeof (GMenuLinkIterPrivate));
+}
+
+GMenuAttributeIter *
+g_menu_model_item_iterate_attributes (GMenuModelItem *item)
+{
+  return g_menu_model_iterate_item_attributes (item->model, item->index_);
+}
+
+GVariant *
+g_menu_model_item_get_attribute_value_by_name (GMenuModelItem     *item,
+                                               const gchar        *attribute_name,
+                                               const GVariantType *expected_type)
+{
+  return g_menu_model_get_item_attribute_value_by_name (item->model, item->index_, attribute_name, expected_type);
+}
+
+gboolean
+g_menu_model_item_get_attribute_by_name (GMenuModelItem *item,
+                                         const gchar    *attribute_name,
+                                         const gchar    *format_string,
+                                         ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_model_get_item_attribute_value_by_name (item->model, item->index_, attribute_name, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+GVariant *
+g_menu_model_item_get_attribute_value (GMenuModelItem     *item,
+                                       GQuark              attribute_quark,
+                                       const GVariantType *expected_type)
+{
+  return g_menu_model_get_item_attribute_value (item->model, item->index_, attribute_quark, expected_type);
+}
+
+gboolean
+g_menu_model_item_get_attribute (GMenuModelItem *item,
+                                 GQuark          attribute_quark,
+                                 const gchar    *format_string,
+                                 ...)
+{
+  const GVariantType *expected_type;
+  GVariant *value;
+  va_list ap;
+
+  expected_type = NULL; /* XXX devine the type, ensure no '&' */
+
+  value = g_menu_model_get_item_attribute_value (item->model, item->index_, attribute_quark, expected_type);
+  if (value == NULL)
+    return FALSE;
+
+  va_start (ap, format_string);
+  g_variant_get_va (value, format_string, NULL, &ap);
+  g_variant_unref (value);
+  va_end (ap);
+
+  return TRUE;
+}
+
+GMenuLinkIter *
+g_menu_model_item_iterate_links (GMenuModelItem *item)
+{
+  return g_menu_model_iterate_item_links (item->model, item->index_);
+}
+
+GMenuModel *
+g_menu_model_item_get_link_by_name (GMenuModelItem *item,
+                                    const gchar    *link_name)
+{
+  return g_menu_model_get_item_link_by_name (item->model, item->index_, link_name);
+}
+
+GMenuModel *
+g_menu_model_item_get_link (GMenuModelItem *item,
+                            GQuark          link_quark)
+{
+  return g_menu_model_get_item_link (item->model, item->index_, link_quark);
+}
diff --git a/gio/gmenumodel.h b/gio/gmenumodel.h
new file mode 100644
index 0000000..053e83b
--- /dev/null
+++ b/gio/gmenumodel.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __G_MENU_MODEL_H__
+#define __G_MENU_MODEL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_MENU_ATTRIBUTE_ACTION                             (g_menu_model_get_action_quark ())
+#define G_MENU_ATTRIBUTE_TARGET                             (g_menu_model_get_target_quark ())
+#define G_MENU_ATTRIBUTE_LABEL                              (g_menu_model_get_label_quark ())
+#define G_MENU_LINK_SUBMENU                                 (g_menu_model_get_submenu_quark ())
+#define G_MENU_LINK_SECTION                                 (g_menu_model_get_section_quark ())
+
+GQuark                  g_menu_model_get_action_quark                   (void) G_GNUC_CONST;
+GQuark                  g_menu_model_get_target_quark                   (void) G_GNUC_CONST;
+GQuark                  g_menu_model_get_label_quark                    (void) G_GNUC_CONST;
+GQuark                  g_menu_model_get_submenu_quark                  (void) G_GNUC_CONST;
+GQuark                  g_menu_model_get_section_quark                  (void) G_GNUC_CONST;
+
+
+#define G_TYPE_MENU_MODEL                                   (g_menu_model_get_type ())
+#define G_MENU_MODEL(inst)                                  (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MENU_MODEL, GMenuModel))
+#define G_MENU_MODEL_CLASS(class)                           (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MENU_MODEL, GMenuModelClass))
+#define G_IS_MENU_MODEL(inst)                               (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU_MODEL))
+#define G_IS_MENU_MODEL_CLASS(class)                        (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MENU_MODEL))
+#define G_MENU_MODEL_GET_CLASS(inst)                        (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MENU_MODEL, GMenuModelClass))
+
+typedef struct _GMenuModelPrivate                           GMenuModelPrivate;
+typedef struct _GMenuModelClass                             GMenuModelClass;
+typedef struct _GMenuModel                                  GMenuModel;
+
+typedef struct _GMenuAttributeIterPrivate                   GMenuAttributeIterPrivate;
+typedef struct _GMenuAttributeIterClass                     GMenuAttributeIterClass;
+typedef struct _GMenuAttributeIter                          GMenuAttributeIter;
+
+typedef struct _GMenuLinkIterPrivate                        GMenuLinkIterPrivate;
+typedef struct _GMenuLinkIterClass                          GMenuLinkIterClass;
+typedef struct _GMenuLinkIter                               GMenuLinkIter;
+
+typedef struct _GMenuModelItem                              GMenuModelItem;
+
+struct _GMenuModel
+{
+  GObject            parent_instance;
+  GMenuModelPrivate *priv;
+};
+
+struct _GMenuModelClass
+{
+  GObjectClass parent_class;
+
+  gboolean              (*is_mutable)                        (GMenuModel         *model);
+  gint                  (*get_n_items)                       (GMenuModel         *model);
+
+  void                  (*get_item_attributes)               (GMenuModel         *model,
+                                                              gint                item_index,
+                                                              GHashTable        **quark_table,
+                                                              GHashTable        **string_table,
+                                                              GVariant          **dictionary);
+  GMenuAttributeIter *  (*iterate_item_attributes)           (GMenuModel         *model,
+                                                              gint                item_index);
+  GVariant *            (*get_item_attribute_value_by_name) (GMenuModel          *model,
+                                                             gint                 item_index,
+                                                             const gchar         *attribute_name,
+                                                             const GVariantType  *expected_type);
+  GVariant *            (*get_item_attribute_value)         (GMenuModel          *model,
+                                                             gint                 item_index,
+                                                             GQuark               attribute_quark,
+                                                             const GVariantType  *expected_type);
+
+  void                  (*get_item_links)                   (GMenuModel          *model,
+                                                             gint                 item_index,
+                                                             GHashTable         **quark_table,
+                                                             GHashTable         **string_table);
+  GMenuLinkIter *       (*iterate_item_links)               (GMenuModel          *model,
+                                                             gint                 item_index);
+  GMenuModel *          (*get_item_link_by_name)            (GMenuModel          *model,
+                                                             gint                 item_index,
+                                                             const gchar         *link_name);
+  GMenuModel *          (*get_item_link)                    (GMenuModel          *model,
+                                                             gint                 item_index,
+                                                             GQuark               link_quark);
+};
+
+GType                   g_menu_model_get_type                           (void) G_GNUC_CONST;
+
+gboolean                g_menu_model_is_mutable                         (GMenuModel         *model);
+gint                    g_menu_model_get_n_items                        (GMenuModel         *model);
+void                    g_menu_model_get_item                           (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         GMenuModelItem     *item);
+
+GMenuAttributeIter *    g_menu_model_iterate_item_attributes            (GMenuModel         *model,
+                                                                         gint                item_index);
+GVariant *              g_menu_model_get_item_attribute_value_by_name   (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         const gchar        *attribute_name,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_model_get_item_attribute_by_name         (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         const gchar        *attribute_name,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GVariant *              g_menu_model_get_item_attribute_value           (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         GQuark              attribute_quark,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_model_get_item_attribute                 (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         GQuark              attribute_quark,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GMenuLinkIter *         g_menu_model_iterate_item_links                 (GMenuModel         *model,
+                                                                         gint                item_index);
+GMenuModel *            g_menu_model_get_item_link_by_name              (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         const gchar        *link_name);
+GMenuModel *            g_menu_model_get_item_link                      (GMenuModel         *model,
+                                                                         gint                item_index,
+                                                                         GQuark              link_quark);
+
+void                    g_menu_model_items_changed                      (GMenuModel         *model,
+                                                                         gint                position,
+                                                                         gint                removed,
+                                                                         gint                added);
+
+
+#define G_TYPE_MENU_ATTRIBUTE_ITER                          (g_menu_attribute_iter_get_type ())
+#define G_MENU_ATTRIBUTE_ITER(inst)                         (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MENU_ATTRIBUTE_ITER, GMenuAttributeIter))
+#define G_MENU_ATTRIBUTE_ITER_CLASS(class)                  (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MENU_ATTRIBUTE_ITER, GMenuAttributeIterClass))
+#define G_IS_MENU_ATTRIBUTE_ITER(inst)                      (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU_ATTRIBUTE_ITER))
+#define G_IS_MENU_ATTRIBUTE_ITER_CLASS(class)               (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MENU_ATTRIBUTE_ITER))
+#define G_MENU_ATTRIBUTE_ITER_GET_CLASS(inst)               (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MENU_ATTRIBUTE_ITER, GMenuAttributeIterClass))
+
+struct _GMenuAttributeIter
+{
+  GObject parent_instance;
+  GMenuAttributeIterPrivate *priv;
+};
+
+struct _GMenuAttributeIterClass
+{
+  GObjectClass parent_class;
+
+  gboolean      (*get_next) (GMenuAttributeIter  *iter,
+                             const gchar        **name,
+                             GVariant           **value);
+};
+
+GType                   g_menu_attribute_iter_get_type                  (void) G_GNUC_CONST;
+
+gboolean                g_menu_attribute_iter_get_next                  (GMenuAttributeIter  *iter,
+                                                                         const gchar        **name,
+                                                                         GVariant           **value);
+gboolean                g_menu_attribute_iter_next                      (GMenuAttributeIter  *iter);
+const gchar *           g_menu_attribute_iter_get_name                  (GMenuAttributeIter  *iter);
+GVariant *              g_menu_attribute_iter_get_value                 (GMenuAttributeIter  *iter);
+
+
+#define G_TYPE_MENU_LINK_ITER                               (g_menu_link_iter_get_type ())
+#define G_MENU_LINK_ITER(inst)                              (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MENU_LINK_ITER, GMenuLinkIter))
+#define G_MENU_LINK_ITER_CLASS(class)                       (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MENU_LINK_ITER, GMenuLinkIterClass))
+#define G_IS_MENU_LINK_ITER(inst)                           (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU_LINK_ITER))
+#define G_IS_MENU_LINK_ITER_CLASS(class)                    (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MENU_LINK_ITER))
+#define G_MENU_LINK_ITER_GET_CLASS(inst)                    (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MENU_LINK_ITER, GMenuLinkIterClass))
+
+struct _GMenuLinkIter
+{
+  GObject parent_instance;
+  GMenuLinkIterPrivate *priv;
+};
+
+struct _GMenuLinkIterClass
+{
+  GObjectClass parent_class;
+
+  gboolean      (*get_next) (GMenuLinkIter  *iter,
+                             const gchar   **name,
+                             GMenuModel    **value);
+};
+
+GType                   g_menu_link_iter_get_type                       (void) G_GNUC_CONST;
+
+gboolean                g_menu_link_iter_get_next                       (GMenuLinkIter  *iter,
+                                                                         const gchar   **name,
+                                                                         GMenuModel    **value);
+gboolean                g_menu_link_iter_next                           (GMenuLinkIter  *iter);
+const gchar *           g_menu_link_iter_get_name                       (GMenuLinkIter  *iter);
+GMenuModel *            g_menu_link_iter_get_value                      (GMenuLinkIter  *iter);
+
+struct _GMenuModelItem
+{
+  /*< private >*/
+  GMenuModel *model;
+  gint index_;
+};
+
+GMenuAttributeIter *    g_menu_model_item_iterate_attributes            (GMenuModelItem     *item);
+GVariant *              g_menu_model_item_get_attribute_value_by_name   (GMenuModelItem     *item,
+                                                                         const gchar        *attribute_name,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_model_item_get_attribute_by_name         (GMenuModelItem     *item,
+                                                                         const gchar        *attribute_name,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GVariant *              g_menu_model_item_get_attribute_value           (GMenuModelItem     *item,
+                                                                         GQuark              attribute_quark,
+                                                                         const GVariantType *expected_type);
+gboolean                g_menu_model_item_get_attribute                 (GMenuModelItem     *item,
+                                                                         GQuark              attribute_quark,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+GMenuLinkIter *         g_menu_model_item_iterate_links                 (GMenuModelItem     *item);
+GMenuModel *            g_menu_model_item_get_link_by_name              (GMenuModelItem     *item,
+                                                                         const gchar        *link_name);
+GMenuModel *            g_menu_model_item_get_link                      (GMenuModelItem     *item,
+                                                                         GQuark              link_quark);
+
+G_END_DECLS
+
+#endif /* __G_MENU_MODEL_H__ */



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