From: Matthias Clasen Date: Sun, 27 Nov 2011 03:46:51 +0000 (-0500) Subject: Add GMenuModel X-Git-Tag: 2.31.4~98 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=12a39a05d3085783923a3613d6a93987732d4d0a;p=platform%2Fupstream%2Fglib.git Add GMenuModel --- diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am index 600f6e4..ea65c7f 100644 --- a/docs/reference/gio/Makefile.am +++ b/docs/reference/gio/Makefile.am @@ -97,7 +97,9 @@ MKDB_OPTIONS = --output-format=xml --sgml-mode --name-space=g \ # Images to copy into HTML directory HTML_IMAGES = \ - gvfs-overview.png + gvfs-overview.png \ + menu-example.png \ + menu-model.png content_files = \ version.xml \ diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 06f8717..55c8ab1 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -200,6 +200,7 @@ + Extending GIO diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index afe290d..ccdcff4 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -3541,3 +3541,79 @@ G_IS_NETWORK_MONITOR G_NETWORK_MONITOR_GET_INTERFACE +
+gmenumodel +GMenuModel +g_menu_model_is_mutable +g_menu_model_get_n_items +g_menu_model_get_item + + +G_MENU_ATTRIBUTE_ACTION +G_MENU_ATTRIBUTE_LABEL +G_MENU_ATTRIBUTE_TARGET +G_MENU_LINK_SECTION +G_MENU_LINK_SUBMENU + + +g_menu_model_get_item_attribute_value +g_menu_model_get_item_attribute +g_menu_model_get_item_link +g_menu_model_iterate_item_attributes +g_menu_model_iterate_item_links + + +g_menu_model_items_changed + + +GMenuAttributeIter +g_menu_attribute_iter_get_next +g_menu_attribute_iter_get_name +g_menu_attribute_iter_get_value +g_menu_attribute_iter_next + + +GMenuLinkIter +g_menu_link_iter_get_name +g_menu_link_iter_get_next +g_menu_link_iter_get_value +g_menu_link_iter_next + + +g_menu_attribute_iter_get_type +g_menu_link_iter_get_type +g_menu_model_get_type +g_menu_model_get_label_quark +g_menu_model_get_action_quark +g_menu_model_get_section_quark +g_menu_model_get_submenu_quark +g_menu_model_get_target_quark + + +GMenuModelClass +GMenuModelPrivate +G_TYPE_MENU_MODEL +G_MENU_MODEL +G_IS_MENU_MODEL +G_MENU_MODEL_CLASS +G_IS_MENU_MODEL_CLASS +G_MENU_MODEL_GET_CLASS + +GMenuAttributeIterClass +GMenuAttributeIterPrivate +G_TYPE_MENU_LINK_ITER +G_MENU_LINK_ITER +G_IS_MENU_LINK_ITER +G_MENU_LINK_ITER_CLASS +G_IS_MENU_LINK_ITER_CLASS +G_MENU_LINK_ITER_GET_CLASS + +GMenuLinkIterClass +GMenuLinkIterPrivate +G_TYPE_MENU_ATTRIBUTE_ITER +G_MENU_ATTRIBUTE_ITER +G_IS_MENU_ATTRIBUTE_ITER +G_MENU_ATTRIBUTE_ITER_CLASS +G_IS_MENU_ATTRIBUTE_ITER_CLASS +G_MENU_ATTRIBUTE_ITER_GET_CLASS +
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index 0100102..5730e16 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -46,6 +46,9 @@ g_local_directory_monitor_get_type 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_link_iter_get_type +g_menu_model_get_type g_mount_get_type g_mount_operation_get_type g_native_volume_monitor_get_type @@ -122,3 +125,8 @@ g_dbus_signal_info_get_type g_dbus_method_info_get_type g_dbus_interface_info_get_type g_dbus_node_info_get_type +g_menu_model_get_type +g_menu_attribute_iter_get_type +g_menu_link_iter_get_type +g_menu_get_type +g_menu_item_get_type diff --git a/docs/reference/gio/menu-example.png b/docs/reference/gio/menu-example.png new file mode 100644 index 0000000..91aeccf Binary files /dev/null and b/docs/reference/gio/menu-example.png differ diff --git a/docs/reference/gio/menu-model.png b/docs/reference/gio/menu-model.png new file mode 100644 index 0000000..a4d9f11 Binary files /dev/null and b/docs/reference/gio/menu-model.png differ diff --git a/gio/Makefile.am b/gio/Makefile.am index a3f5e83..9e5e0a4 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -132,7 +132,9 @@ application_headers = \ gdbusactiongroup.h \ gactiongroupexporter.h \ gapplicationcommandline.h \ - gapplication.h + gapplication.h \ + gmenumodel.h \ + $(NULL) application_sources = \ gactiongroup.c \ @@ -144,7 +146,9 @@ application_sources = \ gapplicationcommandline.c \ gapplicationimpl.h \ gapplicationimpl-dbus.c \ - gapplication.c + gapplication.c \ + gmenumodel.c \ + $(NULL) local_sources = \ glocaldirectorymonitor.c \ diff --git a/gio/gio.h b/gio/gio.h index 9db5288..b7be6df 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -146,6 +146,7 @@ #include #include #include +#include #undef __GIO_GIO_H_INSIDE__ diff --git a/gio/gio.symbols b/gio/gio.symbols index 7a8742d..596715e 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1603,3 +1603,25 @@ 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_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_link_hash_iter_get_type +g_menu_link_iter_get_name +g_menu_link_iter_get_next +g_menu_link_iter_get_type +g_menu_link_iter_get_value +g_menu_link_iter_next +g_menu_model_get_item_attribute +g_menu_model_get_item_attribute_value +g_menu_model_get_item_link +g_menu_model_get_n_items +g_menu_model_get_type +g_menu_model_is_mutable +g_menu_model_items_changed +g_menu_model_items_changed_signal +g_menu_model_iterate_item_attributes +g_menu_model_iterate_item_links diff --git a/gio/giotypes.h b/gio/giotypes.h index 8db258c..c417ea8 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -58,6 +58,8 @@ typedef struct _GSettingsBackend GSettingsBackend; typedef struct _GSettings GSettings; typedef struct _GPermission GPermission; +typedef struct _GMenuModel GMenuModel; + /** * GDrive: * diff --git a/gio/gmenumodel.c b/gio/gmenumodel.c new file mode 100644 index 0000000..4b6d4bc --- /dev/null +++ b/gio/gmenumodel.c @@ -0,0 +1,954 @@ +/* + * 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 + */ + +#include "gmenumodel.h" + +/** + * SECTION:gmenumodel + * @title: GMenuModel + * @short_description: An abstract class representing the contents of a menu + * @see_also: #GActionGroup + * + * #GMenuModel represents the contents of a menu -- an ordered list of + * menu items. The items are associated with actions, which can be + * activated through them. Items can be grouped in sections, and may + * have submenus associated with them. Both items and sections usually + * have some representation data, such as labels or icons. The type of + * the associated action (ie whether it is stateful, and what kind of + * state it has) can influence the representation of the item. + * + * The conceptual model of menus in #GMenuModel is hierarchical: + * sections and submenus are again represented by #GMenuModels. + * Menus themselves do not define their own roles. Rather, the role + * of a particular #GMenuModel is defined by the item that references + * it (or, in the case of the 'root' menu, is defined by the context + * in which it is used). + * + * As an example, consider the visible portions of the menu in + * . + * + * + * + * There are 8 "menus" visible in the screenshot: one menubar, two + * submenus and 5 sections: + * + * the toplevel menubar (containing 4 items) + * the View submenu (containing 3 sections) + * the first section of the View submenu (containing 2 items) + * the second section of the View submenu (containing 1 item) + * the final section of the View submenu (containing 1 item) + * the Highlight Mode submenu (containing 2 sections) + * the Sources section (containing 2 items) + * the Markup section (containing 2 items) + * + * + * illustrates the conceptual connection between + * these 8 menus. Each large block in the figure represents a menu and the + * smaller blocks within the large block represent items in that menu. Some + * items contain references to other menus. + * + * + * + * Notice that the separators visible in + * appear nowhere in . This is because + * separators are not explicitly represented in the menu model. Instead, + * a separator is inserted between any two non-empty sections of a menu. + * Section items can have labels just like any other item. In that case, + * a display system may show a section header instead of a separator. + * + * The motivation for this abstract model of application controls is + * that modern user interfaces tend to make these controls available + * outside the application. Examples include global menus, jumplists, + * dash boards, etc. To support such uses, it is necessary to 'export' + * information about actions and their representation in menus, which + * is exactly what the + * GActionGroup exporter + * and the + * GMenuModel exporter + * do for #GActionGroup and #GMenuModel. The client-side counterparts + * to make use of the exported information are #GDBusActionGroup and + * #GMenuProxy. + * + * The API of #GMenuModel is very generic, with iterators for the + * attributes and links of an item, see g_menu_model_iterate_item_attributes() + * and g_menu_model_iterate_item_links(). The 'standard' attributes and + * link types have predefined names: %G_MENU_ATTRIBUTE_LABEL, + * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, %G_MENU_LINK_SECTION + * and %G_MENU_LINK_SUBMENU. + * + * FIXME: explain how items are associated with actions. + * + * While a wide variety of stateful actions is possible, the following + * is the minimum that is expected to be supported by all users of exported + * menu information: + * + * an action with no parameter type and no state + * an action with no parameter type and boolean state + * an action with string parameter type and string state + * + * + * Stateless + * + * A stateless action typically corresponds to an ordinary menu item. + * + * + * Selecting such a menu item will activate the action (with no parameter). + * + * + * + * Boolean State + * + * An action with a boolean state will most typically be used with a "toggle" + * or "switch" menu item. The state can be set directly, but activating the + * action (with no parameter) results in the state being toggled. + * + * + * Selecting a toggle menu item will activate the action. The menu item should + * be rendered as "checked" when the state is true. + * + * + * + * String Parameter and State + * + * Actions with string parameters and state will most typically be used to + * represent an enumerated choice over the items available for a group of + * radio menu items. Activating the action with a string parameter is + * equivalent to setting that parameter as the state. + * + * + * Radio menu items, in addition to being associated with the action, will + * have a target value. Selecting that menu item will result in activation + * of the action with the target value as the parameter. The menu item should + * be rendered as "selected" when the state of the action is equal to the + * target value of the menu item. + * + * + */ + +/** + * GMenuModel: + * + * #GMenuModel is an opaque structure type. You must access it using the + * functions below. + **/ + +/** + * GMenuAttributeIter: + * + * #GMenuAttributeIter is an opaque structure type. You must access it + * using the functions below. + **/ + +/** + * GMenuLinkIter: + * + * #GMenuLinkIter is an opaque structure type. You must access it using + * the functions below. + **/ + +typedef struct +{ + GMenuLinkIter parent_instance; + GHashTableIter iter; + GHashTable *table; +} 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 **out_name, + GMenuModel **value) +{ + GMenuLinkHashIter *iter = (GMenuLinkHashIter *) link_iter; + gpointer keyptr, valueptr; + + if (!g_hash_table_iter_next (&iter->iter, &keyptr, &valueptr)) + return FALSE; + + *out_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; +} 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; + + *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; +} + +G_DEFINE_ABSTRACT_TYPE (GMenuModel, g_menu_model, G_TYPE_OBJECT) + + +guint g_menu_model_items_changed_signal; + +static GMenuAttributeIter * +g_menu_model_real_iterate_item_attributes (GMenuModel *model, + gint item_index) +{ + GHashTable *table = NULL; + GMenuAttributeIter *result; + + G_MENU_MODEL_GET_CLASS (model)->get_item_attributes (model, item_index, &table); + + if (table) + { + GMenuAttributeHashIter *iter = g_object_new (g_menu_attribute_hash_iter_get_type (), NULL); + g_hash_table_iter_init (&iter->iter, table); + iter->table = g_hash_table_ref (table); + 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 (table != NULL) + g_hash_table_unref (table); + + return result; +} + +static GVariant * +g_menu_model_real_get_item_attribute_value (GMenuModel *model, + gint item_index, + const gchar *attribute, + const GVariantType *expected_type) +{ + GHashTable *table = NULL; + GVariant *value = NULL; + + G_MENU_MODEL_GET_CLASS (model) + ->get_item_attributes (model, item_index, &table); + + if (table != NULL) + { + value = g_hash_table_lookup (table, attribute); + + if (value != NULL) + { + if (expected_type == NULL || g_variant_is_of_type (value, expected_type)) + value = g_variant_ref (value); + else + value = NULL; + } + } + else + g_assert_not_reached (); + + if (table != NULL) + g_hash_table_unref (table); + + return value; +} + +static GMenuLinkIter * +g_menu_model_real_iterate_item_links (GMenuModel *model, + gint item_index) +{ + GHashTable *table = NULL; + GMenuLinkIter *result; + + G_MENU_MODEL_GET_CLASS (model) + ->get_item_links (model, item_index, &table); + + if (table) + { + GMenuLinkHashIter *iter = g_object_new (g_menu_link_hash_iter_get_type (), NULL); + g_hash_table_iter_init (&iter->iter, table); + iter->table = g_hash_table_ref (table); + 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 (table != NULL) + g_hash_table_unref (table); + + return result; +} + +static GMenuModel * +g_menu_model_real_get_item_link (GMenuModel *model, + gint item_index, + const gchar *link) +{ + GHashTable *table = NULL; + GMenuModel *value = NULL; + + G_MENU_MODEL_GET_CLASS (model) + ->get_item_links (model, item_index, &table); + + if (table != NULL) + value = g_hash_table_lookup (table, link); + else + g_assert_not_reached (); + + if (table != NULL) + g_hash_table_unref (table); + + return value ? g_object_ref (value) : 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 = g_menu_model_real_get_item_attribute_value; + class->iterate_item_links = g_menu_model_real_iterate_item_links; + class->get_item_link = g_menu_model_real_get_item_link; + + /** + * GMenuModel::items-changed: + * @model: the #GMenuModel that is changing + * @position: the position of the change + * @removed: the number of items removed + * @added: the number of items added + * + * Emitted when a change has occured to the menu. + * + * The only changes that can occur to a menu is that items are removed + * or added. Items may not change (except by being removed and added + * back in the same location). This signal is capable of describing + * both of those changes (at the same time). + * + * The signal means that starting at the index @position, @removed + * items were removed and @added items were added in their place. If + * @removed is zero then only items were added. If @added is zero + * then only items were removed. + * + * As an example, if the menu contains items a, b, c, d (in that + * order) and the signal (2, 1, 3) occurs then the new composition of + * the menu will be a, b, _, _, _, d (with each _ representing some + * new item). + * + * Signal handlers may query the model (particularly the added items) + * and expect to see the results of the modification that is being + * reported. The signal is emitted after the modification. + **/ + 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); +} + +/** + * g_menu_model_is_mutable: + * @model: a #GMenuModel + * + * Queries if @model is mutable. + * + * An immutable #GMenuModel will never emit the #GMenuModel::items-changed + * signal. Consumers of the model may make optimisations accordingly. + * + * Returns: %TRUE if the model is mutable (ie: "items-changed" may be + * emitted). + **/ +gboolean +g_menu_model_is_mutable (GMenuModel *model) +{ + return G_MENU_MODEL_GET_CLASS (model) + ->is_mutable (model); +} + +/** + * g_menu_model_get_n_items: + * @model: a #GMenuModel + * + * Query the number of items in @model. + * + * Returns: the number of items + **/ +gint +g_menu_model_get_n_items (GMenuModel *model) +{ + return G_MENU_MODEL_GET_CLASS (model) + ->get_n_items (model); +} + +/** + * g_menu_model_iterate_item_attributes: + * @model: a #GMenuModel + * @item_index: the index of the item + * + * Creates a #GMenuAttributeIter to iterate over the attributes of + * the item at position @item_index in @model. + * + * You must free the iterator with g_object_unref() when you are done. + * + * Returns: (transfer full): a new #GMenuAttributeIter + */ +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); +} + +/** + * g_menu_model_get_item_attribute_value: + * @model: a #GMenuModel + * @item_index: the index of the item + * @attribute: the attribute to query + * @expected_type: (allow-none): the expected type of the attribute, or + * %NULL + * + * Queries the item at position @item_index in @model for the attribute + * specified by @attribute. + * + * If @expected_type is non-%NULL then it specifies the expected type of + * the attribute. If it is %NULL then any type will be accepted. + * + * If the attribute exists and matches @expected_type (or if the + * expected type is unspecified) then the value is returned. + * + * If the attribute does not exist, or does not match the expected type + * then %NULL is returned. + * + * Returns: (transfer full): the value of the attribute + **/ +GVariant * +g_menu_model_get_item_attribute_value (GMenuModel *model, + gint item_index, + const gchar *attribute, + const GVariantType *expected_type) +{ + return G_MENU_MODEL_GET_CLASS (model) + ->get_item_attribute_value (model, item_index, attribute, expected_type); +} + +/** + * g_menu_model_get_item_attribute: + * @model: a #GMenuModel + * @item_index: the index of the item + * @attribute: the attribute to query + * @format_string: a #GVariant format string + * @...: positional parameters, as per @format_string + * + * Queries item at position @item_index in @model for the attribute + * specified by @attribute. + * + * If the attribute exists and matches the #GVariantType corresponding + * to @format_string then @format_string is used to deconstruct the + * value into the positional parameters and %TRUE is returned. + * + * If the attribute does not exist, or it does exist but has the wrong + * type, then the positional parameters are ignored and %FALSE is + * returned. + * + * Returns: %TRUE if the named attribute was found with the expected + * type + **/ +gboolean +g_menu_model_get_item_attribute (GMenuModel *model, + gint item_index, + const gchar *attribute, + 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, 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; +} + +/** + * g_menu_model_iterate_item_links: + * @model: a #GMenuModel + * @item_index: the index of the item + * + * Creates a #GMenuLinkIter to iterate over the links of the item at + * position @item_index in @model. + * + * You must free the iterator with g_object_unref() when you are done. + * + * Returns: (transfer full): a new #GMenuLinkIter + **/ +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); +} + +/** + * g_menu_model_get_item_link: + * @model: a #GMenuModel + * @item_index: the index of the item + * @link: the link to query + * + * Queries the item at position @item_index in @model for the link + * specified by @link. + * + * If the link exists, the linked #GMenuModel is returned. If the link + * does not exist, %NULL is returned. + * + * Returns: (transfer full): the linked #GMenuModel, or %NULL + **/ +GMenuModel * +g_menu_model_get_item_link (GMenuModel *model, + gint item_index, + const gchar *link) +{ + return G_MENU_MODEL_GET_CLASS (model) + ->get_item_link (model, item_index, link); +} + +/** + * g_menu_model_items_changed: + * @model: a #GMenuModel + * @position: the position of the change + * @removed: the number of items removed + * @added: the number of items added + * + * Requests emission of the #GMenuMode::items-changed signal on @model. + * + * This function should never be called except by #GMenuModel + * subclasses. Any other calls to this function will very likely lead + * to a violation of the interface of the model. + * + * The implementation should update its internal representation of the + * menu before emitting the signal. The implementation should further + * expect to receive queries about the new state of the menu (and + * particularly added menu items) while signal handlers are running. + * + * The implementation must dispatch this call directly from a mainloop + * entry and not in response to calls -- particularly those from the + * #GMenuModel API. Said another way: the menu must not change while + * user code is running without returning to the mainloop. + **/ +void +g_menu_model_items_changed (GMenuModel *model, + gint position, + gint removed, + gint added) +{ + g_signal_emit (model, g_menu_model_items_changed_signal, 0, position, removed, added); +} + +G_DEFINE_ABSTRACT_TYPE (GMenuAttributeIter, g_menu_attribute_iter, G_TYPE_OBJECT) + +struct _GMenuAttributeIterPrivate +{ + GQuark name; + GVariant *value; + gboolean valid; +}; + +/** + * g_menu_attribute_iter_get_next: + * @iter: a #GMenuAttributeIter + * @out_name: (out) (allow-none) (transfer none): the type of the attribute + * @value: (out) (allow-none) (transfer full): the attribute value + * + * This function combines g_menu_attribute_iter_next() with + * g_menu_attribute_iter_get_name() and g_menu_attribute_iter_get_value(). + * + * First the iterator is advanced to the next (possibly first) attribute. + * If that fails, then %FALSE is returned and there are no other + * effects. + * + * If successful, @name and @value are set to the name and value of the + * attribute that has just been advanced to. At this point, + * g_menu_item_get_name() and g_menu_item_get_value() will return the + * same values again. + * + * The value returned in @name remains valid for as long as the iterator + * remains at the current position. The value returned in @value must + * be unreffed using g_variant_unref() when it is no longer in use. + * + * Returns: %TRUE on success, or %FALSE if there is no additional + * attribute + **/ +gboolean +g_menu_attribute_iter_get_next (GMenuAttributeIter *iter, + const gchar **out_name, + GVariant **value) +{ + const gchar *name; + + if (iter->priv->value) + { + g_variant_unref (iter->priv->value); + iter->priv->value = NULL; + } + + iter->priv->valid = G_MENU_ATTRIBUTE_ITER_GET_CLASS (iter) + ->get_next (iter, &name, &iter->priv->value); + + if (iter->priv->valid) + { + iter->priv->name = g_quark_from_string (name); + if (out_name) + *out_name = g_quark_to_string (iter->priv->name); + + if (value) + *value = g_variant_ref (iter->priv->value); + } + + return iter->priv->valid; +} + +/** + * g_menu_attribute_iter_next: + * @iter: a #GMenuAttributeIter + * + * Attempts to advance the iterator to the next (possibly first) + * attribute. + * + * %TRUE is returned on success, or %FALSE if there are no more + * attributes. + * + * You must call this function when you first acquire the iterator + * to advance it to the first attribute (and determine if the first + * attribute exists at all). + * + * Returns: %TRUE on success, or %FALSE when there are no more attributes + **/ +gboolean +g_menu_attribute_iter_next (GMenuAttributeIter *iter) +{ + return g_menu_attribute_iter_get_next (iter, NULL, NULL); +} + +/** + * g_menu_attribute_iter_get_name: + * @iter: a #GMenuAttributeIter + * + * Gets the name of the attribute at the current iterator position, as + * a string. + * + * The iterator is not advanced. + * + * Returns: the name of the attribute + **/ +const gchar * +g_menu_attribute_iter_get_name (GMenuAttributeIter *iter) +{ + g_return_val_if_fail (iter->priv->valid, 0); + + return g_quark_to_string (iter->priv->name); +} + +/** + * g_menu_attribute_iter_get_value: + * @iter: a #GMenuAttributeIter + * + * Gets the value of the attribute at the current iterator position. + * + * The iterator is not advanced. + * + * Returns: (transfer full): the value of the current attribute + **/ +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 +{ + GQuark name; + GMenuModel *value; + gboolean valid; +}; + +/** + * g_menu_link_iter_get_next: + * @iter: a #GMenuLinkIter + * @out_name: (out) (allow-none) (transfer none): the name of the link + * @value: (out) (allow-none) (transfer full): the linked #GMenuModel + * + * This function combines g_menu_link_iter_next() with + * g_menu_link_iter_get_name() and g_menu_link_iter_get_value(). + * + * First the iterator is advanced to the next (possibly first) link. If + * that fails, then %FALSE is returned and there are no other effects. + * + * If successful, @out_name and @value are set to the name and #GMenuModel + * of the link that has just been advanced to. At this point, + * g_menu_item_get_name() and g_menu_item_get_value() will return the + * same values again. + * + * The value returned in @out_name remains valid for as long as the iterator + * remains at the current position. The value returned in @value must + * be unreffed using g_object_unref() when it is no longer in use. + * + * Returns: %TRUE on success, or %FALSE if there is no additional link + **/ +gboolean +g_menu_link_iter_get_next (GMenuLinkIter *iter, + const gchar **out_name, + GMenuModel **value) +{ + const gchar *name; + + if (iter->priv->value) + { + g_object_unref (iter->priv->value); + iter->priv->value = NULL; + } + + iter->priv->valid = G_MENU_LINK_ITER_GET_CLASS (iter) + ->get_next (iter, &name, &iter->priv->value); + + if (iter->priv->valid) + { + g_assert (name != NULL); + + iter->priv->name = g_quark_from_string (name); + if (out_name) + *out_name = g_quark_to_string (iter->priv->name); + + if (value) + *value = g_object_ref (iter->priv->value); + } + + return iter->priv->valid; +} + +/** + * g_menu_link_iter_next: + * @iter: a #GMenuLinkIter + * + * Attempts to advance the iterator to the next (possibly first) + * link. + * + * %TRUE is returned on success, or %FALSE if there are no more links. + * + * You must call this function when you first acquire the iterator to + * advance it to the first link (and determine if the first link exists + * at all). + * + * Returns: %TRUE on success, or %FALSE when there are no more links + **/ +gboolean +g_menu_link_iter_next (GMenuLinkIter *iter) +{ + return g_menu_link_iter_get_next (iter, NULL, NULL); +} + +/** + * g_menu_link_iter_get_name: + * @iter: a #GMenuLinkIter + * + * Gets the name of the link at the current iterator position. + * + * The iterator is not advanced. + * + * Returns: the type of the link + **/ +const gchar * +g_menu_link_iter_get_name (GMenuLinkIter *iter) +{ + g_return_val_if_fail (iter->priv->valid, 0); + + return g_quark_to_string (iter->priv->name); +} + +/** + * g_menu_link_iter_get_value: + * @iter: a #GMenuLinkIter + * + * Gets the linked #GMenuModel at the current iterator position. + * + * The iterator is not advanced. + * + * Returns: (transfer full): the #GMenuModel that is linked to + **/ +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)); +} diff --git a/gio/gmenumodel.h b/gio/gmenumodel.h new file mode 100644 index 0000000..689c2ce --- /dev/null +++ b/gio/gmenumodel.h @@ -0,0 +1,195 @@ +/* + * 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 + */ + +#ifndef __G_MENU_MODEL_H__ +#define __G_MENU_MODEL_H__ + +#include + +#include + +G_BEGIN_DECLS + +#define G_MENU_ATTRIBUTE_ACTION "action" +#define G_MENU_ATTRIBUTE_TARGET "target" +#define G_MENU_ATTRIBUTE_LABEL "label" + +#define G_MENU_LINK_SUBMENU "submenu" +#define G_MENU_LINK_SECTION "section" + +#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 _GMenuAttributeIterPrivate GMenuAttributeIterPrivate; +typedef struct _GMenuAttributeIterClass GMenuAttributeIterClass; +typedef struct _GMenuAttributeIter GMenuAttributeIter; + +typedef struct _GMenuLinkIterPrivate GMenuLinkIterPrivate; +typedef struct _GMenuLinkIterClass GMenuLinkIterClass; +typedef struct _GMenuLinkIter GMenuLinkIter; + +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 **attributes); + GMenuAttributeIter * (*iterate_item_attributes) (GMenuModel *model, + gint item_index); + GVariant * (*get_item_attribute_value) (GMenuModel *model, + gint item_index, + const gchar *attribute, + const GVariantType *expected_type); + void (*get_item_links) (GMenuModel *model, + gint item_index, + GHashTable **links); + GMenuLinkIter * (*iterate_item_links) (GMenuModel *model, + gint item_index); + GMenuModel * (*get_item_link) (GMenuModel *model, + gint item_index, + const gchar *link); +}; + +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); + +GMenuAttributeIter * g_menu_model_iterate_item_attributes (GMenuModel *model, + gint item_index); +GVariant * g_menu_model_get_item_attribute_value (GMenuModel *model, + gint item_index, + const gchar *attribute, + const GVariantType *expected_type); +gboolean g_menu_model_get_item_attribute (GMenuModel *model, + gint item_index, + const gchar *attribute, + const gchar *format_string, + ...); +GMenuLinkIter * g_menu_model_iterate_item_links (GMenuModel *model, + gint item_index); +GMenuModel * g_menu_model_get_item_link (GMenuModel *model, + gint item_index, + const gchar *link); + +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 **out_type, + GVariant **value); +}; + +GType g_menu_attribute_iter_get_type (void) G_GNUC_CONST; + +gboolean g_menu_attribute_iter_get_next (GMenuAttributeIter *iter, + const gchar **out_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 **out_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 **out_link, + 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); + +G_END_DECLS + +#endif /* __G_MENU_MODEL_H__ */