diff options
author | Matthias Clasen <mclasen@redhat.com> | 2011-11-26 22:48:24 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2011-12-08 18:05:12 -0500 |
commit | 13f59777350f120dd82d1bd36e15155057b073c3 (patch) | |
tree | ca42d74dd2b97189b91475abde6974558a6486ed /gio/gmenu.c | |
parent | 12a39a05d3085783923a3613d6a93987732d4d0a (diff) | |
download | glib-13f59777350f120dd82d1bd36e15155057b073c3.tar.gz |
Add GMenu
Diffstat (limited to 'gio/gmenu.c')
-rw-r--r-- | gio/gmenu.c | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/gio/gmenu.c b/gio/gmenu.c new file mode 100644 index 000000000..d11f7a3d8 --- /dev/null +++ b/gio/gmenu.c @@ -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; +} |