Add GMenu
authorMatthias Clasen <mclasen@redhat.com>
Sun, 27 Nov 2011 03:48:24 +0000 (22:48 -0500)
committerRyan Lortie <desrt@desrt.ca>
Thu, 8 Dec 2011 23:05:12 +0000 (18:05 -0500)
docs/reference/gio/gio-docs.xml
docs/reference/gio/gio-sections.txt
docs/reference/gio/gio.types
gio/Makefile.am
gio/gio.h
gio/gio.symbols
gio/gmenu.c [new file with mode: 0644]
gio/gmenu.h [new file with mode: 0644]

index 55c8ab1c220307bb210fab362446ad0ee0b0cc4d..2fd3ef5d6eac2fb95f7aa9039d3d06e8de83a909 100644 (file)
         <xi:include href="xml/gactiongroupexporter.xml"/>
         <xi:include href="xml/gdbusactiongroup.xml"/>
         <xi:include href="xml/gmenumodel.xml"/>
+        <xi:include href="xml/gmenu.xml"/>
     </chapter>
     <chapter id="extending">
         <title>Extending GIO</title>
index ccdcff49a806a2baf8fe7d7e3dc0402fb7eac030..0d81f508beadf6ebdca363d4cf56e8e8fb6c3674 100644 (file)
@@ -3541,6 +3541,68 @@ G_IS_NETWORK_MONITOR
 G_NETWORK_MONITOR_GET_INTERFACE
 </SECTION>
 
+<SECTION>
+<FILE>gmenu</FILE>
+GMenu
+g_menu_new
+g_menu_freeze
+
+<SUBSECTION>
+g_menu_insert
+g_menu_prepend
+g_menu_append
+
+<SUBSECTION>
+g_menu_insert_item
+g_menu_append_item
+g_menu_prepend_item
+
+<SUBSECTION>
+g_menu_insert_section
+g_menu_prepend_section
+g_menu_append_section
+
+<SUBSECTION>
+g_menu_append_submenu
+g_menu_insert_submenu
+g_menu_prepend_submenu
+
+<SUBSECTION>
+g_menu_remove
+
+<SUBSECTION>
+GMenuItem
+g_menu_item_new
+g_menu_item_new_section
+g_menu_item_new_submenu
+
+<SUBSECTION>
+g_menu_item_set_label
+g_menu_item_set_action_and_target_value
+g_menu_item_set_action_and_target
+g_menu_item_set_detailed_action
+g_menu_item_set_section
+g_menu_item_set_submenu
+
+<SUBSECTION>
+g_menu_item_set_attribute_value
+g_menu_item_set_attribute
+g_menu_item_set_link
+
+<SUBSECTION Private>
+g_menu_item_get_type
+g_menu_get_type
+
+<SUBSECTION Standard>
+G_TYPE_MENU
+G_MENU
+G_IS_MENU
+
+G_TYPE_MENU_ITEM
+G_MENU_ITEM
+G_IS_MENU_ITEM
+</SECTION>
+
 <SECTION>
 <FILE>gmenumodel</FILE>
 GMenuModel
index 5730e16f648ced3c36b7795b13d4ffea8147d3b9..3e0c40b882e5e150b3bb34a83682a04f599fe664 100644 (file)
@@ -47,6 +47,7 @@ g_local_file_monitor_get_type
 g_memory_input_stream_get_type
 g_memory_output_stream_get_type
 g_menu_attribute_iter_get_type
+g_menu_get_type
 g_menu_link_iter_get_type
 g_menu_model_get_type
 g_mount_get_type
index 9e5e0a444925f4b91437f2fe2f07cb439fad0f25..9f96e3cc03c295397f4455b493063b79faeec002 100644 (file)
@@ -134,6 +134,7 @@ application_headers = \
        gapplicationcommandline.h       \
        gapplication.h                  \
        gmenumodel.h                    \
+       gmenu.h                         \
        $(NULL)
 
 application_sources = \
@@ -148,6 +149,7 @@ application_sources = \
        gapplicationimpl-dbus.c                 \
        gapplication.c                          \
        gmenumodel.c                            \
+       gmenu.c                                 \
        $(NULL)
 
 local_sources = \
index b7be6df807e5a3ce54ec3e8ab4f85be420025a45..00ef1e9c17d97a322797f5a4684e83e97c5c33eb 100644 (file)
--- a/gio/gio.h
+++ b/gio/gio.h
 #include <gio/gdbusobjectmanagerserver.h>
 #include <gio/gdbusactiongroup.h>
 #include <gio/gmenumodel.h>
+#include <gio/gmenu.h>
 
 #undef __GIO_GIO_H_INSIDE__
 
index 596715ebef80a89a40f5bd29cea0048b8fab95ec..f9060920ed2cdb6544ff1de10f0772efd5cc6ccc 100644 (file)
@@ -1603,12 +1603,35 @@ g_network_monitor_base_add_network
 g_network_monitor_base_get_type
 g_network_monitor_base_remove_network
 g_network_monitor_base_set_networks
+g_menu_append
+g_menu_append_item
+g_menu_append_section
+g_menu_append_submenu
 g_menu_attribute_hash_iter_get_type
 g_menu_attribute_iter_get_name
 g_menu_attribute_iter_get_next
 g_menu_attribute_iter_get_type
 g_menu_attribute_iter_get_value
 g_menu_attribute_iter_next
+g_menu_freeze
+g_menu_get_type
+g_menu_insert
+g_menu_insert_item
+g_menu_insert_section
+g_menu_insert_submenu
+g_menu_item_get_type
+g_menu_item_new
+g_menu_item_new_section
+g_menu_item_new_submenu
+g_menu_item_set_action_and_target
+g_menu_item_set_action_and_target_value
+g_menu_item_set_attribute
+g_menu_item_set_attribute_value
+g_menu_item_set_detailed_action
+g_menu_item_set_label
+g_menu_item_set_link
+g_menu_item_set_section
+g_menu_item_set_submenu
 g_menu_link_hash_iter_get_type
 g_menu_link_iter_get_name
 g_menu_link_iter_get_next
@@ -1625,3 +1648,9 @@ g_menu_model_items_changed
 g_menu_model_items_changed_signal
 g_menu_model_iterate_item_attributes
 g_menu_model_iterate_item_links
+g_menu_new
+g_menu_prepend
+g_menu_prepend_item
+g_menu_prepend_section
+g_menu_prepend_submenu
+g_menu_remove
diff --git a/gio/gmenu.c b/gio/gmenu.c
new file mode 100644 (file)
index 0000000..d11f7a3
--- /dev/null
@@ -0,0 +1,1019 @@
+/*
+ * Copyright © 2011 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gmenu.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gmenu
+ * @title: GMenu
+ * @short_description: A simple implementation of GMenuModel
+ *
+ * #GMenu is a simple implementation of #GMenuModel.
+ * You populate a #GMenu by adding #GMenuItem instances to it.
+ *
+ * There are some convenience functions to allow you to directly
+ * add items (avoiding #GMenuItem) for the common cases. To add
+ * a regular item, use g_menu_insert(). To add a section, use
+ * g_menu_insert_section(). To add a submenu, use
+ * g_menu_insert_submenu().
+ *
+ * Often it is more convenient to create a #GMenu from an XML
+ * fragment, using <link linkend="gio-GMenu-Markup">GMenu Markup</link>.
+ */
+
+/**
+ * GMenu:
+ *
+ * #GMenu is an opaque structure type.  You must access it using the
+ * functions below.
+ **/
+
+/**
+ * GMenuItem:
+ *
+ * #GMenuItem is an opaque structure type.  You must access it using the
+ * functions below.
+ **/
+
+struct _GMenuItem
+{
+  GObject parent_instance;
+
+  GHashTable *attributes;
+  GHashTable *links;
+  gboolean    cow;
+};
+
+typedef GObjectClass GMenuItemClass;
+
+struct _GMenu
+{
+  GMenuModel parent_instance;
+
+  GArray   *items;
+  gboolean  mutable;
+};
+
+typedef GMenuModelClass GMenuClass;
+
+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;
+};
+
+static gboolean
+g_menu_is_mutable (GMenuModel *model)
+{
+  GMenu *menu = G_MENU (model);
+
+  return menu->mutable;
+}
+
+static gint
+g_menu_get_n_items (GMenuModel *model)
+{
+  GMenu *menu = G_MENU (model);
+
+  return menu->items->len;
+}
+
+static void
+g_menu_get_item_attributes (GMenuModel  *model,
+                            gint         position,
+                            GHashTable **table)
+{
+  GMenu *menu = G_MENU (model);
+
+  *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
+}
+
+static void
+g_menu_get_item_links (GMenuModel  *model,
+                       gint         position,
+                       GHashTable **table)
+{
+  GMenu *menu = G_MENU (model);
+
+  *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
+}
+
+/**
+ * g_menu_insert_item:
+ * @menu: a #GMenu
+ * @position: the position at which to insert the item
+ * @item: the #GMenuItem to insert
+ *
+ * Inserts @item into @menu.
+ *
+ * The "insertion" is actually done by copying all of the attribute and
+ * link values of @item and using them to form a new item within @menu.
+ * As such, @item itself is not really inserted, but rather, a menu item
+ * that is exactly the same as the one presently described by @item.
+ *
+ * This means that @item is essentially useless after the insertion
+ * occurs.  Any changes you make to it are ignored unless it is inserted
+ * again (at which point its updated values will be copied).
+ *
+ * You should probably just free @item once you're done.
+ *
+ * There are many convenience functions to take care of common cases.
+ * See g_menu_insert(), g_menu_insert_section() and
+ * g_menu_insert_submenu() as well as "prepend" and "append" variants of
+ * each of these functions.
+ **/
+void
+g_menu_insert_item (GMenu     *menu,
+                    gint       position,
+                    GMenuItem *item)
+{
+  struct item new_item;
+
+  g_return_if_fail (G_IS_MENU (menu));
+  g_return_if_fail (G_IS_MENU_ITEM (item));
+
+  if (position < 0 || position > menu->items->len)
+    position = menu->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->items, position, new_item);
+  g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
+}
+
+/**
+ * g_menu_prepend_item:
+ * @menu: a #GMenu
+ * @item: a #GMenuItem to prepend
+ *
+ * Prepends @item to the start of @menu.
+ *
+ * See g_menu_insert_item() for more information.
+ **/
+void
+g_menu_prepend_item (GMenu     *menu,
+                     GMenuItem *item)
+{
+  g_menu_insert_item (menu, 0, item);
+}
+
+/**
+ * g_menu_append_item:
+ * @menu: a #GMenu
+ * @item: a #GMenuItem to append
+ *
+ * Appends @item to the end of @menu.
+ *
+ * See g_menu_insert_item() for more information.
+ **/
+void
+g_menu_append_item (GMenu     *menu,
+                    GMenuItem *item)
+{
+  g_menu_insert_item (menu, -1, item);
+}
+
+/**
+ * g_menu_freeze:
+ * @menu: a #GMenu
+ *
+ * Marks @menu as frozen.
+ *
+ * After the menu is frozen, it is an error to attempt to make any
+ * changes to it.  In effect this means that the #GMenu API must no
+ * longer be used.
+ *
+ * This function causes g_menu_model_is_mutable() to begin returning
+ * %FALSE, which has some positive performance implications.
+ **/
+void
+g_menu_freeze (GMenu *menu)
+{
+  g_return_if_fail (G_IS_MENU (menu));
+
+  menu->mutable = FALSE;
+}
+
+/**
+ * g_menu_new:
+ *
+ * Creates a new #GMenu.
+ *
+ * The new menu has no items.
+ *
+ * Returns: a new #GMenu
+ **/
+GMenu *
+g_menu_new (void)
+{
+  return g_object_new (G_TYPE_MENU, NULL);
+}
+
+/**
+ * g_menu_insert:
+ * @menu: a #GMenu
+ * @position: the position at which to insert the item
+ * @label: (allow-none): the section label, or %NULL
+ * @detailed_action: (allow-none): the detailed action string, or %NULL
+ *
+ * Convenience function for inserting a normal menu item into @menu.
+ * Combine g_menu_new() and g_menu_insert_item() for a more flexible
+ * alternative.
+ **/
+void
+g_menu_insert (GMenu       *menu,
+               gint         position,
+               const gchar *label,
+               const gchar *detailed_action)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new (label, detailed_action);
+  g_menu_insert_item (menu, position, menu_item);
+  g_object_unref (menu_item);
+}
+
+/**
+ * g_menu_prepend:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @detailed_action: (allow-none): the detailed action string, or %NULL
+ *
+ * Convenience function for prepending a normal menu item to the start
+ * of @menu.  Combine g_menu_new() and g_menu_insert_item() for a more
+ * flexible alternative.
+ **/
+void
+g_menu_prepend (GMenu       *menu,
+                const gchar *label,
+                const gchar *detailed_action)
+{
+  g_menu_insert (menu, 0, label, detailed_action);
+}
+
+/**
+ * g_menu_append:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @detailed_action: (allow-none): the detailed action string, or %NULL
+ *
+ * Convenience function for appending a normal menu item to the end of
+ * @menu.  Combine g_menu_new() and g_menu_insert_item() for a more
+ * flexible alternative.
+ **/
+void
+g_menu_append (GMenu       *menu,
+               const gchar *label,
+               const gchar *detailed_action)
+{
+  g_menu_insert (menu, -1, label, detailed_action);
+}
+
+/**
+ * g_menu_insert_section:
+ * @menu: a #GMenu
+ * @position: the position at which to insert the item
+ * @label: (allow-none): the section label, or %NULL
+ * @section: a #GMenuModel with the items of the section
+ *
+ * Convenience function for inserting a section menu item into @menu.
+ * Combine g_menu_new_section() and g_menu_insert_item() for a more
+ * flexible alternative.
+ **/
+void
+g_menu_insert_section (GMenu       *menu,
+                       gint         position,
+                       const gchar *label,
+                       GMenuModel  *section)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new_section (label, section);
+  g_menu_insert_item (menu, position, menu_item);
+  g_object_unref (menu_item);
+}
+
+
+/**
+ * g_menu_prepend_section:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @section: a #GMenuModel with the items of the section
+ *
+ * Convenience function for prepending a section menu item to the start
+ * of @menu.  Combine g_menu_new_section() and g_menu_insert_item() for
+ * a more flexible alternative.
+ **/
+void
+g_menu_prepend_section (GMenu       *menu,
+                        const gchar *label,
+                        GMenuModel  *section)
+{
+  g_menu_insert_section (menu, 0, label, section);
+}
+
+/**
+ * g_menu_append_section:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @section: a #GMenuModel with the items of the section
+ *
+ * Convenience function for appending a section menu item to the end of
+ * @menu.  Combine g_menu_new_section() and g_menu_insert_item() for a
+ * more flexible alternative.
+ **/
+void
+g_menu_append_section (GMenu       *menu,
+                       const gchar *label,
+                       GMenuModel  *section)
+{
+  g_menu_insert_section (menu, -1, label, section);
+}
+
+/**
+ * g_menu_insert_submenu:
+ * @menu: a #GMenu
+ * @position: the position at which to insert the item
+ * @label: (allow-none): the section label, or %NULL
+ * @submenu: a #GMenuModel with the items of the submenu
+ *
+ * Convenience function for inserting a submenu menu item into @menu.
+ * Combine g_menu_new_submenu() and g_menu_insert_item() for a more
+ * flexible alternative.
+ **/
+void
+g_menu_insert_submenu (GMenu       *menu,
+                       gint         position,
+                       const gchar *label,
+                       GMenuModel  *submenu)
+{
+  GMenuItem *menu_item;
+
+  menu_item = g_menu_item_new_submenu (label, submenu);
+  g_menu_insert_item (menu, position, menu_item);
+  g_object_unref (menu_item);
+}
+
+/**
+ * g_menu_prepend_submenu:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @submenu: a #GMenuModel with the items of the submenu
+ *
+ * Convenience function for prepending a submenu menu item to the start
+ * of @menu.  Combine g_menu_new_submenu() and g_menu_insert_item() for
+ * a more flexible alternative.
+ **/
+void
+g_menu_prepend_submenu (GMenu       *menu,
+                        const gchar *label,
+                        GMenuModel  *submenu)
+{
+  g_menu_insert_submenu (menu, 0, label, submenu);
+}
+
+/**
+ * g_menu_append_submenu:
+ * @menu: a #GMenu
+ * @label: (allow-none): the section label, or %NULL
+ * @submenu: a #GMenuModel with the items of the submenu
+ *
+ * Convenience function for appending a submenu menu item to the end of
+ * @menu.  Combine g_menu_new_submenu() and g_menu_insert_item() for a
+ * more flexible alternative.
+ **/
+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);
+}
+
+/**
+ * g_menu_remove:
+ * @menu: a #GMenu
+ * @position: the position of the item to remove
+ *
+ * Removes an item from the menu.
+ *
+ * @position gives the index of the item to remove.
+ *
+ * It is an error if position is not in range the range from 0 to one
+ * less than the number of items in the menu.
+ *
+ * It is not possible to remove items by identity since items are added
+ * to the menu simply by copying their links and attributes (ie:
+ * identity of the item itself is not preserved).
+ **/
+void
+g_menu_remove (GMenu *menu,
+               gint   index_)
+{
+  g_return_if_fail (G_IS_MENU (menu));
+  g_return_if_fail (0 <= index_ && index_ < menu->items->len);
+
+  g_menu_clear_item (&g_array_index (menu->items, struct item, index_));
+  g_array_remove_index (menu->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->items->len;
+  items = (struct item *) g_array_free (menu->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->items = g_array_new (FALSE, FALSE, sizeof (struct item));
+  menu->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;
+}
+
+
+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 (g_str_hash, g_str_equal, g_free, (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, g_strdup (key), g_variant_ref (val));
+      g_hash_table_unref (menu_item->attributes);
+      menu_item->attributes = new;
+
+      new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) 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, g_strdup (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 (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+  menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  menu_item->cow = FALSE;
+}
+
+static void
+g_menu_item_class_init (GMenuItemClass *class)
+{
+  class->finalize = g_menu_item_finalize;
+}
+
+/**
+ * g_menu_item_set_attribute_value:
+ * @menu_item: a #GMenuItem
+ * @attribute: the attribute to set
+ * @value: (allow-none): a #GVariant to use as the value, or %NULL
+ *
+ * Sets or unsets an attribute on @menu_item.
+ *
+ * The attribute to set or unset is specified by @attribute.
+ *
+ * If @value is non-%NULL then it is used as the new value for the
+ * attribute.  If @value is %NULL then the attribute is unset.
+ *
+ * See also g_menu_item_set_attribute() for a more convenient way to do
+ * the same.
+ **/
+void
+g_menu_item_set_attribute_value (GMenuItem   *menu_item,
+                                 const gchar *attribute,
+                                 GVariant    *value)
+{
+  g_return_if_fail (G_IS_MENU_ITEM (menu_item));
+  g_return_if_fail (attribute != NULL);
+
+  g_menu_item_clear_cow (menu_item);
+
+  if (value != NULL)
+    g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
+  else
+    g_hash_table_remove (menu_item->attributes, attribute);
+}
+
+/**
+ * g_menu_item_set_attribute:
+ * @menu_item: a #GMenuItem
+ * @attribute: the attribute to set
+ * @format_string: (allow-none): a #GVariant format string, or %NULL
+ * @...: positional parameters, as per @format_string
+ *
+ * Sets or unsets an attribute on @menu_item.
+ *
+ * The attribute to set or unset is specified by @attribute.
+ *
+ * If @format_string is non-%NULL then the proper position parameters
+ * are collected to create a #GVariant instance to use as the attribute
+ * value.  If it is %NULL then the positional parameterrs are ignored
+ * and the named attribute is unset.
+ *
+ * See also g_menu_item_set_attribute_value() for an equivalent call
+ * that directly accepts a #GVariant.
+ **/
+void
+g_menu_item_set_attribute (GMenuItem   *menu_item,
+                           const gchar *attribute,
+                           const gchar *format_string,
+                           ...)
+{
+  GVariant *value;
+
+  if (format_string != NULL)
+    {
+      va_list ap;
+
+      va_start (ap, format_string);
+      value = g_variant_new_va (format_string, NULL, &ap);
+      va_end (ap);
+    }
+  else
+    value = NULL;
+
+  g_menu_item_set_attribute_value (menu_item, attribute, value);
+}
+
+/**
+ * g_menu_item_set_link:
+ * @menu_item: a #GMenuItem
+ * @link: type of link to establish or unset
+ * @model: (allow-none): the #GMenuModel to link to (or %NULL to unset)
+ *
+ * Creates a link from @menu_item to @link if non-%NULL, or unsets it.
+ *
+ * Links are used to establish a relationship between a particular menu
+ * item and another menu.  For example, %G_MENU_LINK_SUBMENU is used to
+ * associate a submenu with a particular menu item.
+ */
+void
+g_menu_item_set_link (GMenuItem   *menu_item,
+                      const gchar *link,
+                      GMenuModel  *model)
+{
+  g_return_if_fail (G_IS_MENU_ITEM (menu_item));
+  g_return_if_fail (link != NULL);
+
+  g_menu_item_clear_cow (menu_item);
+
+  if (model != NULL)
+    g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
+  else
+    g_hash_table_remove (menu_item->links, link);
+}
+
+/**
+ * g_menu_item_set_label:
+ * @menu_item: a #GMenuItem
+ * @label: (allow-none): the label to set, or %NULL to unset
+ *
+ * Sets or unsets the "label" attribute of @menu_item.
+ *
+ * If @label is non-%NULL it is used as the label for the menu item.  If
+ * it is %NULL then the label attribute is unset.
+ **/
+void
+g_menu_item_set_label (GMenuItem   *menu_item,
+                       const gchar *label)
+{
+  GVariant *value;
+
+  if (label != NULL)
+    value = g_variant_new_string (label);
+  else
+    value = NULL;
+
+  g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
+}
+
+/**
+ * g_menu_item_set_submenu:
+ * @menu_item: a #GMenuItem
+ * @submenu: (allow-none): a #GMenuModel, or %NULL
+ *
+ * Sets or unsets the "submenu" link of @menu_item to @submenu.
+ *
+ * If @submenu is non-%NULL, it is linked to.  If it is %NULL then the
+ * link is unset.
+ *
+ * The effect of having one menu appear as a submenu of another is
+ * exactly as it sounds.
+ **/
+void
+g_menu_item_set_submenu (GMenuItem  *menu_item,
+                         GMenuModel *submenu)
+{
+  g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
+}
+
+/**
+ * g_menu_item_set_section:
+ * @menu_item: a #GMenuItem
+ * @section: (allow-none): a #GMenuModel, or %NULL
+ *
+ * Sets or unsets the "section" link of @menu_item to @section.
+ *
+ * The effect of having one menu appear as a section of another is
+ * exactly as it sounds: the items from @section become a direct part of
+ * the menu that @menu_item is added to.  See g_menu_item_new_section()
+ * for more information about what it means for a menu item to be a
+ * section.
+ **/
+void
+g_menu_item_set_section (GMenuItem  *menu_item,
+                         GMenuModel *section)
+{
+  g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
+}
+
+/**
+ * g_menu_item_set_action_and_target_value:
+ * @menu_item: a #GMenuItem
+ * @action: (allow-none): the name of the action for this item
+ * @target_value: (allow-none): a #GVariant to use as the action target
+ *
+ * Sets or unsets the "action" and "target" attributes of @menu_item.
+ *
+ * If @action is %NULL then both the "action" and "target" attributes
+ * are unset (and @target_value is ignored).
+ *
+ * If @action is non-%NULL then the "action" attribute is set.  The
+ * "target" attribute is then set to the value of @target_value if it is
+ * non-%NULL or unset otherwise.
+ *
+ * Normal menu items (ie: not submenu, section or other custom item
+ * types) are expected to have the "action" attribute set to identify
+ * the action that they are associated with.  The state type of the
+ * action help to determine the disposition of the menu item.  See
+ * #GAction and #GActionGroup for an overview of actions.
+ *
+ * In general, clicking on the menu item will result in activation of
+ * the named action with the "target" attribute given as the parameter
+ * to the action invocation.  If the "target" attribute is not set then
+ * the action is invoked with no parameter.
+ *
+ * If the action has no state then the menu item is usually drawn as a
+ * plain menu item (ie: with no additional decoration).
+ *
+ * If the action has a boolean state then the menu item is usually drawn
+ * as a toggle menu item (ie: with a checkmark or equivalent
+ * indication).  The item should be marked as 'toggled' or 'checked'
+ * when the boolean state is %TRUE.
+ *
+ * If the action has a string state then the menu item is usually drawn
+ * as a radio menu item (ie: with a radio bullet or equivalent
+ * indication).  The item should be marked as 'selected' when the string
+ * state is equal to the value of the @target property.
+ *
+ * See g_menu_item_set_action_and_target() or
+ * g_menu_item_set_detailed_action() for two equivalent calls that are
+ * probably more convenient for most uses.
+ */
+void
+g_menu_item_set_action_and_target_value (GMenuItem   *menu_item,
+                                         const gchar *action,
+                                         GVariant    *target_value)
+{
+  GVariant *action_value;
+
+  if (action != NULL)
+    {
+      action_value = g_variant_new_string (action);
+    }
+  else
+    {
+      action_value = NULL;
+      target_value = NULL;
+    }
+
+  g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
+  g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
+}
+
+/**
+ * g_menu_item_set_action_and_target:
+ * @menu_item: a #GMenuItem
+ * @action: (allow-none): the name of the action for this item
+ * @format_string: (allow-none): a GVariant format string
+ * @...: positional parameters, as per @format_string
+ *
+ * Sets or unsets the "action" and "target" attributes of @menu_item.
+ *
+ * If @action is %NULL then both the "action" and "target" attributes
+ * are unset (and @format_string is ignored along with the positional
+ * parameters).
+ *
+ * If @action is non-%NULL then the "action" attribute is set.
+ * @format_string is then inspected.  If it is non-%NULL then the proper
+ * position parameters are collected to create a #GVariant instance to
+ * use as the target value.  If it is %NULL then the positional
+ * parameters are ignored and the "target" attribute is unset.
+ *
+ * See also g_menu_item_set_action_and_target_value() for an equivalent
+ * call that directly accepts a #GVariant.  See
+ * g_menu_item_set_detailed_action() for a more convenient version that
+ * works with string-typed targets.
+ *
+ * See also g_menu_item_set_action_and_target_value() for a
+ * description of the semantics of the action and target attributes.
+ */
+void
+g_menu_item_set_action_and_target (GMenuItem   *menu_item,
+                                   const gchar *action,
+                                   const gchar *format_string,
+                                   ...)
+{
+  GVariant *value;
+
+  if (format_string != NULL)
+    {
+      va_list ap;
+
+      va_start (ap, format_string);
+      value = g_variant_new_va (format_string, NULL, &ap);
+      va_end (ap);
+    }
+  else
+    value = NULL;
+
+  g_menu_item_set_action_and_target_value (menu_item, action, value);
+}
+
+/**
+ * g_menu_item_set_detailed_action:
+ * @menu_item: a #GMenuItem
+ * @detailed_action: the "detailed" action string
+ *
+ * Sets the "action" and possibly the "target" attribute of @menu_item.
+ *
+ * If @detailed_action contains a double colon ("::") then it is used as
+ * a separator between an action name and a target string.  In this
+ * case, this call is equivalent to calling
+ * g_menu_item_set_action_and_target() with the part before the "::" and
+ * g_menu_item_set_target_value() with a string-type #GVariant
+ * containing the part following the "::".
+ *
+ * If @detailed_action doesn't contain "::" then the action is set to
+ * the given string (verbatim) and the target value is unset.
+ *
+ * See g_menu_item_set_action_and_target() or
+ * g_menu_item_set_action_and_target_value() for more flexible (but
+ * slightly less convenient) alternatives.
+ *
+ * See also g_menu_set_action_and_target_value() for a description of
+ * the semantics of the action and target attributes.
+ **/
+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);
+}
+
+/**
+ * g_menu_item_new:
+ * @label: (allow-none): the section label, or %NULL
+ * @detailed_action: (allow-none): the detailed action string, or %NULL
+ *
+ * Creates a new #GMenuItem.
+ *
+ * If @label is non-%NULL it is used to set the "label" attribute of the
+ * new item.
+ *
+ * If @detailed_action is non-%NULL it is used to set the "action" and
+ * possibly the "target" attribute of the new item.  See
+ * g_menu_item_set_detailed_action() for more information.
+ *
+ * Returns: a new #GMenuItem
+ **/
+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;
+}
+
+/**
+ * g_menu_item_new_submenu:
+ * @label: (allow-none): the section label, or %NULL
+ * @submenu: a #GMenuModel with the items of the submenu
+ *
+ * Creates a new #GMenuItem representing a submenu.
+ *
+ * This is a convenience API around g_menu_item_new() and
+ * g_menu_item_set_submenu().
+ *
+ * Returns: a new #GMenuItem
+ **/
+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;
+}
+
+/**
+ * g_menu_item_new_section:
+ * @label: (allow-none): the section label, or %NULL
+ * @section: a #GMenuModel with the items of the section
+ *
+ * Creates a new #GMenuItem representing a section.
+ *
+ * This is a convenience API around g_menu_item_new() and
+ * g_menu_item_set_section().
+ *
+ * The effect of having one menu appear as a section of another is
+ * exactly as it sounds: the items from @section become a direct part of
+ * the menu that @menu_item is added to.
+ *
+ * Visual separation is typically displayed between two non-empty
+ * sections.  If @label is non-%NULL then it will be encorporated into
+ * this visual indication.  This allows for labeled subsections of a
+ * menu.
+ *
+ * As a simple example, consider a typical "Edit" menu from a simple
+ * program.  It probably contains an "Undo" and "Redo" item, followed by
+ * a separator, followed by "Cut", "Copy" and "Paste".
+ *
+ * This would be accomplished by creating three #GMenu instances.  The
+ * first would be populated with the "Undo" and "Redo" items, and the
+ * second with the "Cut", "Copy" and "Paste" items.  The first and
+ * second menus would then be added as submenus of the third.  In XML
+ * format, this would look something like the following:
+ *
+ * <informalexample><programlisting><![CDATA[
+ * <menu id='edit-menu'>
+ *   <section>
+ *     <item label='Undo'/>
+ *     <item label='Redo'/>
+ *   </section>
+ *   <section>
+ *     <item label='Cut'/>
+ *     <item label='Copy'/>
+ *     <item label='Paste'/>
+ *   </section>
+ * </menu>
+ * ]]></programlisting></informalexample>
+ *
+ * The following example is exactly equivalent.  It is more illustrative
+ * of the exact relationship between the menus and items (keeping in
+ * mind that the 'link' element defines a new menu that is linked to the
+ * containing one).  The style of the second example is more verbose and
+ * difficult to read (and therefore not recommended except for the
+ * purpose of understanding what is really going on).
+ *
+ * <informalexample><programlisting><![CDATA[
+ * <menu id='edit-menu'>
+ *   <item>
+ *     <link name='section'>
+ *       <item label='Undo'/>
+ *       <item label='Redo'/>
+ *     </link>
+ *   </item>
+ *   <item>
+ *     <link name='section'>
+ *       <item label='Cut'/>
+ *       <item label='Copy'/>
+ *       <item label='Paste'/>
+ *     </link>
+ *   </item>
+ * </menu>
+ * ]]></programlisting></informalexample>
+ *
+ * Returns: a new #GMenuItem
+ **/
+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 (file)
index 0000000..716832f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2011 Canonical Ltd.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * 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_IS_MENU(inst)                                     (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU))
+
+#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_IS_MENU_ITEM(inst)                                (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MENU_ITEM))
+
+typedef struct _GMenuItem                                   GMenuItem;
+typedef struct _GMenu                                       GMenu;
+
+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                position,
+                                                                         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                position);
+
+void                    g_menu_insert                                   (GMenu              *menu,
+                                                                         gint                position,
+                                                                         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                position,
+                                                                         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                position,
+                                                                         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);
+
+void                    g_menu_item_set_attribute_value                 (GMenuItem          *menu_item,
+                                                                         const char         *attribute,
+                                                                         GVariant           *value);
+void                    g_menu_item_set_attribute                       (GMenuItem          *menu_item,
+                                                                         const char         *attribute,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+void                    g_menu_item_set_link                            (GMenuItem          *menu_item,
+                                                                         const char         *link,
+                                                                         GMenuModel         *model);
+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_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__ */