(root)/
glib-2.79.0/
gio/
gmenu.c
       1  /*
       2   * Copyright © 2011 Canonical Ltd.
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful, but
      12   * WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General Public
      17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * Author: Ryan Lortie <desrt@desrt.ca>
      20   */
      21  
      22  #include "config.h"
      23  
      24  #include "gmenu.h"
      25  
      26  #include "gaction.h"
      27  #include <string.h>
      28  
      29  #include "gicon.h"
      30  
      31  /**
      32   * GMenu:
      33   *
      34   * `GMenu` is a simple implementation of [class@Gio.MenuModel].
      35   * You populate a `GMenu` by adding [class@Gio.MenuItem] instances to it.
      36   *
      37   * There are some convenience functions to allow you to directly
      38   * add items (avoiding [class@Gio.MenuItem]) for the common cases. To add
      39   * a regular item, use [method@Gio.Menu.insert]. To add a section, use
      40   * [method@Gio.Menu.insert_section]. To add a submenu, use
      41   * [method@Gio.Menu.insert_submenu].
      42   *
      43   * Since: 2.32
      44   */
      45  
      46  /**
      47   * GMenuItem:
      48   *
      49   * #GMenuItem is an opaque structure type.  You must access it using the
      50   * functions below.
      51   *
      52   * Since: 2.32
      53   */
      54  
      55  struct _GMenuItem
      56  {
      57    GObject parent_instance;
      58  
      59    GHashTable *attributes;
      60    GHashTable *links;
      61    gboolean    cow;
      62  };
      63  
      64  typedef GObjectClass GMenuItemClass;
      65  
      66  struct _GMenu
      67  {
      68    GMenuModel parent_instance;
      69  
      70    GArray   *items;
      71    gboolean  mutable;
      72  };
      73  
      74  typedef GMenuModelClass GMenuClass;
      75  
      76  G_DEFINE_TYPE (GMenu, g_menu, G_TYPE_MENU_MODEL)
      77  G_DEFINE_TYPE (GMenuItem, g_menu_item, G_TYPE_OBJECT)
      78  
      79  struct item
      80  {
      81    GHashTable *attributes;
      82    GHashTable *links;
      83  };
      84  
      85  static gboolean
      86  g_menu_is_mutable (GMenuModel *model)
      87  {
      88    GMenu *menu = G_MENU (model);
      89  
      90    return menu->mutable;
      91  }
      92  
      93  static gint
      94  g_menu_get_n_items (GMenuModel *model)
      95  {
      96    GMenu *menu = G_MENU (model);
      97  
      98    return menu->items->len;
      99  }
     100  
     101  static void
     102  g_menu_get_item_attributes (GMenuModel  *model,
     103                              gint         position,
     104                              GHashTable **table)
     105  {
     106    GMenu *menu = G_MENU (model);
     107  
     108    *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
     109  }
     110  
     111  static void
     112  g_menu_get_item_links (GMenuModel  *model,
     113                         gint         position,
     114                         GHashTable **table)
     115  {
     116    GMenu *menu = G_MENU (model);
     117  
     118    *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
     119  }
     120  
     121  /**
     122   * g_menu_insert_item:
     123   * @menu: a #GMenu
     124   * @position: the position at which to insert the item
     125   * @item: the #GMenuItem to insert
     126   *
     127   * Inserts @item into @menu.
     128   *
     129   * The "insertion" is actually done by copying all of the attribute and
     130   * link values of @item and using them to form a new item within @menu.
     131   * As such, @item itself is not really inserted, but rather, a menu item
     132   * that is exactly the same as the one presently described by @item.
     133   *
     134   * This means that @item is essentially useless after the insertion
     135   * occurs.  Any changes you make to it are ignored unless it is inserted
     136   * again (at which point its updated values will be copied).
     137   *
     138   * You should probably just free @item once you're done.
     139   *
     140   * There are many convenience functions to take care of common cases.
     141   * See g_menu_insert(), g_menu_insert_section() and
     142   * g_menu_insert_submenu() as well as "prepend" and "append" variants of
     143   * each of these functions.
     144   *
     145   * Since: 2.32
     146   */
     147  void
     148  g_menu_insert_item (GMenu     *menu,
     149                      gint       position,
     150                      GMenuItem *item)
     151  {
     152    struct item new_item;
     153  
     154    g_return_if_fail (G_IS_MENU (menu));
     155    g_return_if_fail (G_IS_MENU_ITEM (item));
     156  
     157    if (position < 0 || (guint) position > menu->items->len)
     158      position = menu->items->len;
     159  
     160    new_item.attributes = g_hash_table_ref (item->attributes);
     161    new_item.links = g_hash_table_ref (item->links);
     162    item->cow = TRUE;
     163  
     164    g_array_insert_val (menu->items, position, new_item);
     165    g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
     166  }
     167  
     168  /**
     169   * g_menu_prepend_item:
     170   * @menu: a #GMenu
     171   * @item: a #GMenuItem to prepend
     172   *
     173   * Prepends @item to the start of @menu.
     174   *
     175   * See g_menu_insert_item() for more information.
     176   *
     177   * Since: 2.32
     178   */
     179  void
     180  g_menu_prepend_item (GMenu     *menu,
     181                       GMenuItem *item)
     182  {
     183    g_menu_insert_item (menu, 0, item);
     184  }
     185  
     186  /**
     187   * g_menu_append_item:
     188   * @menu: a #GMenu
     189   * @item: a #GMenuItem to append
     190   *
     191   * Appends @item to the end of @menu.
     192   *
     193   * See g_menu_insert_item() for more information.
     194   *
     195   * Since: 2.32
     196   */
     197  void
     198  g_menu_append_item (GMenu     *menu,
     199                      GMenuItem *item)
     200  {
     201    g_menu_insert_item (menu, -1, item);
     202  }
     203  
     204  /**
     205   * g_menu_freeze:
     206   * @menu: a #GMenu
     207   *
     208   * Marks @menu as frozen.
     209   *
     210   * After the menu is frozen, it is an error to attempt to make any
     211   * changes to it.  In effect this means that the #GMenu API must no
     212   * longer be used.
     213   *
     214   * This function causes g_menu_model_is_mutable() to begin returning
     215   * %FALSE, which has some positive performance implications.
     216   *
     217   * Since: 2.32
     218   */
     219  void
     220  g_menu_freeze (GMenu *menu)
     221  {
     222    g_return_if_fail (G_IS_MENU (menu));
     223  
     224    menu->mutable = FALSE;
     225  }
     226  
     227  /**
     228   * g_menu_new:
     229   *
     230   * Creates a new #GMenu.
     231   *
     232   * The new menu has no items.
     233   *
     234   * Returns: a new #GMenu
     235   *
     236   * Since: 2.32
     237   */
     238  GMenu *
     239  g_menu_new (void)
     240  {
     241    return g_object_new (G_TYPE_MENU, NULL);
     242  }
     243  
     244  /**
     245   * g_menu_insert:
     246   * @menu: a #GMenu
     247   * @position: the position at which to insert the item
     248   * @label: (nullable): the section label, or %NULL
     249   * @detailed_action: (nullable): the detailed action string, or %NULL
     250   *
     251   * Convenience function for inserting a normal menu item into @menu.
     252   * Combine g_menu_item_new() and g_menu_insert_item() for a more flexible
     253   * alternative.
     254   *
     255   * Since: 2.32
     256   */
     257  void
     258  g_menu_insert (GMenu       *menu,
     259                 gint         position,
     260                 const gchar *label,
     261                 const gchar *detailed_action)
     262  {
     263    GMenuItem *menu_item;
     264  
     265    menu_item = g_menu_item_new (label, detailed_action);
     266    g_menu_insert_item (menu, position, menu_item);
     267    g_object_unref (menu_item);
     268  }
     269  
     270  /**
     271   * g_menu_prepend:
     272   * @menu: a #GMenu
     273   * @label: (nullable): the section label, or %NULL
     274   * @detailed_action: (nullable): the detailed action string, or %NULL
     275   *
     276   * Convenience function for prepending a normal menu item to the start
     277   * of @menu.  Combine g_menu_item_new() and g_menu_insert_item() for a more
     278   * flexible alternative.
     279   *
     280   * Since: 2.32
     281   */
     282  void
     283  g_menu_prepend (GMenu       *menu,
     284                  const gchar *label,
     285                  const gchar *detailed_action)
     286  {
     287    g_menu_insert (menu, 0, label, detailed_action);
     288  }
     289  
     290  /**
     291   * g_menu_append:
     292   * @menu: a #GMenu
     293   * @label: (nullable): the section label, or %NULL
     294   * @detailed_action: (nullable): the detailed action string, or %NULL
     295   *
     296   * Convenience function for appending a normal menu item to the end of
     297   * @menu.  Combine g_menu_item_new() and g_menu_insert_item() for a more
     298   * flexible alternative.
     299   *
     300   * Since: 2.32
     301   */
     302  void
     303  g_menu_append (GMenu       *menu,
     304                 const gchar *label,
     305                 const gchar *detailed_action)
     306  {
     307    g_menu_insert (menu, -1, label, detailed_action);
     308  }
     309  
     310  /**
     311   * g_menu_insert_section:
     312   * @menu: a #GMenu
     313   * @position: the position at which to insert the item
     314   * @label: (nullable): the section label, or %NULL
     315   * @section: a #GMenuModel with the items of the section
     316   *
     317   * Convenience function for inserting a section menu item into @menu.
     318   * Combine g_menu_item_new_section() and g_menu_insert_item() for a more
     319   * flexible alternative.
     320   *
     321   * Since: 2.32
     322   */
     323  void
     324  g_menu_insert_section (GMenu       *menu,
     325                         gint         position,
     326                         const gchar *label,
     327                         GMenuModel  *section)
     328  {
     329    GMenuItem *menu_item;
     330  
     331    menu_item = g_menu_item_new_section (label, section);
     332    g_menu_insert_item (menu, position, menu_item);
     333    g_object_unref (menu_item);
     334  }
     335  
     336  
     337  /**
     338   * g_menu_prepend_section:
     339   * @menu: a #GMenu
     340   * @label: (nullable): the section label, or %NULL
     341   * @section: a #GMenuModel with the items of the section
     342   *
     343   * Convenience function for prepending a section menu item to the start
     344   * of @menu.  Combine g_menu_item_new_section() and g_menu_insert_item() for
     345   * a more flexible alternative.
     346   *
     347   * Since: 2.32
     348   */
     349  void
     350  g_menu_prepend_section (GMenu       *menu,
     351                          const gchar *label,
     352                          GMenuModel  *section)
     353  {
     354    g_menu_insert_section (menu, 0, label, section);
     355  }
     356  
     357  /**
     358   * g_menu_append_section:
     359   * @menu: a #GMenu
     360   * @label: (nullable): the section label, or %NULL
     361   * @section: a #GMenuModel with the items of the section
     362   *
     363   * Convenience function for appending a section menu item to the end of
     364   * @menu.  Combine g_menu_item_new_section() and g_menu_insert_item() for a
     365   * more flexible alternative.
     366   *
     367   * Since: 2.32
     368   */
     369  void
     370  g_menu_append_section (GMenu       *menu,
     371                         const gchar *label,
     372                         GMenuModel  *section)
     373  {
     374    g_menu_insert_section (menu, -1, label, section);
     375  }
     376  
     377  /**
     378   * g_menu_insert_submenu:
     379   * @menu: a #GMenu
     380   * @position: the position at which to insert the item
     381   * @label: (nullable): the section label, or %NULL
     382   * @submenu: a #GMenuModel with the items of the submenu
     383   *
     384   * Convenience function for inserting a submenu menu item into @menu.
     385   * Combine g_menu_item_new_submenu() and g_menu_insert_item() for a more
     386   * flexible alternative.
     387   *
     388   * Since: 2.32
     389   */
     390  void
     391  g_menu_insert_submenu (GMenu       *menu,
     392                         gint         position,
     393                         const gchar *label,
     394                         GMenuModel  *submenu)
     395  {
     396    GMenuItem *menu_item;
     397  
     398    menu_item = g_menu_item_new_submenu (label, submenu);
     399    g_menu_insert_item (menu, position, menu_item);
     400    g_object_unref (menu_item);
     401  }
     402  
     403  /**
     404   * g_menu_prepend_submenu:
     405   * @menu: a #GMenu
     406   * @label: (nullable): the section label, or %NULL
     407   * @submenu: a #GMenuModel with the items of the submenu
     408   *
     409   * Convenience function for prepending a submenu menu item to the start
     410   * of @menu.  Combine g_menu_item_new_submenu() and g_menu_insert_item() for
     411   * a more flexible alternative.
     412   *
     413   * Since: 2.32
     414   */
     415  void
     416  g_menu_prepend_submenu (GMenu       *menu,
     417                          const gchar *label,
     418                          GMenuModel  *submenu)
     419  {
     420    g_menu_insert_submenu (menu, 0, label, submenu);
     421  }
     422  
     423  /**
     424   * g_menu_append_submenu:
     425   * @menu: a #GMenu
     426   * @label: (nullable): the section label, or %NULL
     427   * @submenu: a #GMenuModel with the items of the submenu
     428   *
     429   * Convenience function for appending a submenu menu item to the end of
     430   * @menu.  Combine g_menu_item_new_submenu() and g_menu_insert_item() for a
     431   * more flexible alternative.
     432   *
     433   * Since: 2.32
     434   */
     435  void
     436  g_menu_append_submenu (GMenu       *menu,
     437                         const gchar *label,
     438                         GMenuModel  *submenu)
     439  {
     440    g_menu_insert_submenu (menu, -1, label, submenu);
     441  }
     442  
     443  static void
     444  g_menu_clear_item (struct item *item)
     445  {
     446    if (item->attributes != NULL)
     447      g_hash_table_unref (item->attributes);
     448    if (item->links != NULL)
     449      g_hash_table_unref (item->links);
     450  }
     451  
     452  /**
     453   * g_menu_remove:
     454   * @menu: a #GMenu
     455   * @position: the position of the item to remove
     456   *
     457   * Removes an item from the menu.
     458   *
     459   * @position gives the index of the item to remove.
     460   *
     461   * It is an error if position is not in range the range from 0 to one
     462   * less than the number of items in the menu.
     463   *
     464   * It is not possible to remove items by identity since items are added
     465   * to the menu simply by copying their links and attributes (ie:
     466   * identity of the item itself is not preserved).
     467   *
     468   * Since: 2.32
     469   */
     470  void
     471  g_menu_remove (GMenu *menu,
     472                 gint   position)
     473  {
     474    g_return_if_fail (G_IS_MENU (menu));
     475    g_return_if_fail (0 <= position && (guint) position < menu->items->len);
     476  
     477    g_menu_clear_item (&g_array_index (menu->items, struct item, position));
     478    g_array_remove_index (menu->items, position);
     479    g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0);
     480  }
     481  
     482  /**
     483   * g_menu_remove_all:
     484   * @menu: a #GMenu
     485   *
     486   * Removes all items in the menu.
     487   *
     488   * Since: 2.38
     489   **/
     490  void
     491  g_menu_remove_all (GMenu *menu)
     492  {
     493    gint i, n;
     494  
     495    g_return_if_fail (G_IS_MENU (menu));
     496    n = menu->items->len;
     497  
     498    for (i = 0; i < n; i++)
     499      g_menu_clear_item (&g_array_index (menu->items, struct item, i));
     500    g_array_set_size (menu->items, 0);
     501  
     502    g_menu_model_items_changed (G_MENU_MODEL (menu), 0, n, 0);
     503  }
     504  
     505  static void
     506  g_menu_finalize (GObject *object)
     507  {
     508    GMenu *menu = G_MENU (object);
     509    struct item *items;
     510    gint n_items;
     511    gint i;
     512  
     513    n_items = menu->items->len;
     514    items = (struct item *) g_array_free (menu->items, FALSE);
     515    for (i = 0; i < n_items; i++)
     516      g_menu_clear_item (&items[i]);
     517    g_free (items);
     518  
     519    G_OBJECT_CLASS (g_menu_parent_class)
     520      ->finalize (object);
     521  }
     522  
     523  static void
     524  g_menu_init (GMenu *menu)
     525  {
     526    menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
     527    menu->mutable = TRUE;
     528  }
     529  
     530  static void
     531  g_menu_class_init (GMenuClass *class)
     532  {
     533    GMenuModelClass *model_class = G_MENU_MODEL_CLASS (class);
     534    GObjectClass *object_class = G_OBJECT_CLASS (class);
     535  
     536    object_class->finalize = g_menu_finalize;
     537  
     538    model_class->is_mutable = g_menu_is_mutable;
     539    model_class->get_n_items = g_menu_get_n_items;
     540    model_class->get_item_attributes = g_menu_get_item_attributes;
     541    model_class->get_item_links = g_menu_get_item_links;
     542  }
     543  
     544  
     545  static void
     546  g_menu_item_clear_cow (GMenuItem *menu_item)
     547  {
     548    if (menu_item->cow)
     549      {
     550        GHashTableIter iter;
     551        GHashTable *new;
     552        gpointer key;
     553        gpointer val;
     554  
     555        new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
     556        g_hash_table_iter_init (&iter, menu_item->attributes);
     557        while (g_hash_table_iter_next (&iter, &key, &val))
     558          g_hash_table_insert (new, g_strdup (key), g_variant_ref (val));
     559        g_hash_table_unref (menu_item->attributes);
     560        menu_item->attributes = new;
     561  
     562        new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
     563        g_hash_table_iter_init (&iter, menu_item->links);
     564        while (g_hash_table_iter_next (&iter, &key, &val))
     565          g_hash_table_insert (new, g_strdup (key), g_object_ref (val));
     566        g_hash_table_unref (menu_item->links);
     567        menu_item->links = new;
     568  
     569        menu_item->cow = FALSE;
     570      }
     571  }
     572  
     573  static void
     574  g_menu_item_finalize (GObject *object)
     575  {
     576    GMenuItem *menu_item = G_MENU_ITEM (object);
     577  
     578    g_hash_table_unref (menu_item->attributes);
     579    g_hash_table_unref (menu_item->links);
     580  
     581    G_OBJECT_CLASS (g_menu_item_parent_class)
     582      ->finalize (object);
     583  }
     584  
     585  static void
     586  g_menu_item_init (GMenuItem *menu_item)
     587  {
     588    menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
     589    menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
     590    menu_item->cow = FALSE;
     591  }
     592  
     593  static void
     594  g_menu_item_class_init (GMenuItemClass *class)
     595  {
     596    class->finalize = g_menu_item_finalize;
     597  }
     598  
     599  /* We treat attribute names the same as GSettings keys:
     600   * - only lowercase ascii, digits and '-'
     601   * - must start with lowercase
     602   * - must not end with '-'
     603   * - no consecutive '-'
     604   * - not longer than 1024 chars
     605   */
     606  static gboolean
     607  valid_attribute_name (const gchar *name)
     608  {
     609    gint i;
     610  
     611    if (!g_ascii_islower (name[0]))
     612      return FALSE;
     613  
     614    for (i = 1; name[i]; i++)
     615      {
     616        if (name[i] != '-' &&
     617            !g_ascii_islower (name[i]) &&
     618            !g_ascii_isdigit (name[i]))
     619          return FALSE;
     620  
     621        if (name[i] == '-' && name[i + 1] == '-')
     622          return FALSE;
     623      }
     624  
     625    if (name[i - 1] == '-')
     626      return FALSE;
     627  
     628    if (i > 1024)
     629      return FALSE;
     630  
     631    return TRUE;
     632  }
     633  
     634  /**
     635   * g_menu_item_set_attribute_value:
     636   * @menu_item: a #GMenuItem
     637   * @attribute: the attribute to set
     638   * @value: (nullable): a #GVariant to use as the value, or %NULL
     639   *
     640   * Sets or unsets an attribute on @menu_item.
     641   *
     642   * The attribute to set or unset is specified by @attribute. This
     643   * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
     644   * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
     645   * attribute name.
     646   * Attribute names are restricted to lowercase characters, numbers
     647   * and '-'. Furthermore, the names must begin with a lowercase character,
     648   * must not end with a '-', and must not contain consecutive dashes.
     649   *
     650   * must consist only of lowercase
     651   * ASCII characters, digits and '-'.
     652   *
     653   * If @value is non-%NULL then it is used as the new value for the
     654   * attribute.  If @value is %NULL then the attribute is unset. If
     655   * the @value #GVariant is floating, it is consumed.
     656   *
     657   * See also g_menu_item_set_attribute() for a more convenient way to do
     658   * the same.
     659   *
     660   * Since: 2.32
     661   */
     662  void
     663  g_menu_item_set_attribute_value (GMenuItem   *menu_item,
     664                                   const gchar *attribute,
     665                                   GVariant    *value)
     666  {
     667    g_return_if_fail (G_IS_MENU_ITEM (menu_item));
     668    g_return_if_fail (attribute != NULL);
     669    g_return_if_fail (valid_attribute_name (attribute));
     670  
     671    g_menu_item_clear_cow (menu_item);
     672  
     673    if (value != NULL)
     674      g_hash_table_insert (menu_item->attributes, g_strdup (attribute), g_variant_ref_sink (value));
     675    else
     676      g_hash_table_remove (menu_item->attributes, attribute);
     677  }
     678  
     679  /**
     680   * g_menu_item_set_attribute:
     681   * @menu_item: a #GMenuItem
     682   * @attribute: the attribute to set
     683   * @format_string: (nullable): a #GVariant format string, or %NULL
     684   * @...: positional parameters, as per @format_string
     685   *
     686   * Sets or unsets an attribute on @menu_item.
     687   *
     688   * The attribute to set or unset is specified by @attribute. This
     689   * can be one of the standard attribute names %G_MENU_ATTRIBUTE_LABEL,
     690   * %G_MENU_ATTRIBUTE_ACTION, %G_MENU_ATTRIBUTE_TARGET, or a custom
     691   * attribute name.
     692   * Attribute names are restricted to lowercase characters, numbers
     693   * and '-'. Furthermore, the names must begin with a lowercase character,
     694   * must not end with a '-', and must not contain consecutive dashes.
     695   *
     696   * If @format_string is non-%NULL then the proper position parameters
     697   * are collected to create a #GVariant instance to use as the attribute
     698   * value.  If it is %NULL then the positional parameterrs are ignored
     699   * and the named attribute is unset.
     700   *
     701   * See also g_menu_item_set_attribute_value() for an equivalent call
     702   * that directly accepts a #GVariant.
     703   *
     704   * Since: 2.32
     705   */
     706  void
     707  g_menu_item_set_attribute (GMenuItem   *menu_item,
     708                             const gchar *attribute,
     709                             const gchar *format_string,
     710                             ...)
     711  {
     712    GVariant *value;
     713  
     714    if (format_string != NULL)
     715      {
     716        va_list ap;
     717  
     718        va_start (ap, format_string);
     719        value = g_variant_new_va (format_string, NULL, &ap);
     720        va_end (ap);
     721      }
     722    else
     723      value = NULL;
     724  
     725    g_menu_item_set_attribute_value (menu_item, attribute, value);
     726  }
     727  
     728  /**
     729   * g_menu_item_set_link:
     730   * @menu_item: a #GMenuItem
     731   * @link: type of link to establish or unset
     732   * @model: (nullable): the #GMenuModel to link to (or %NULL to unset)
     733   *
     734   * Creates a link from @menu_item to @model if non-%NULL, or unsets it.
     735   *
     736   * Links are used to establish a relationship between a particular menu
     737   * item and another menu.  For example, %G_MENU_LINK_SUBMENU is used to
     738   * associate a submenu with a particular menu item, and %G_MENU_LINK_SECTION
     739   * is used to create a section. Other types of link can be used, but there
     740   * is no guarantee that clients will be able to make sense of them.
     741   * Link types are restricted to lowercase characters, numbers
     742   * and '-'. Furthermore, the names must begin with a lowercase character,
     743   * must not end with a '-', and must not contain consecutive dashes.
     744   *
     745   * Since: 2.32
     746   */
     747  void
     748  g_menu_item_set_link (GMenuItem   *menu_item,
     749                        const gchar *link,
     750                        GMenuModel  *model)
     751  {
     752    g_return_if_fail (G_IS_MENU_ITEM (menu_item));
     753    g_return_if_fail (link != NULL);
     754    g_return_if_fail (valid_attribute_name (link));
     755  
     756    g_menu_item_clear_cow (menu_item);
     757  
     758    if (model != NULL)
     759      g_hash_table_insert (menu_item->links, g_strdup (link), g_object_ref (model));
     760    else
     761      g_hash_table_remove (menu_item->links, link);
     762  }
     763  
     764  /**
     765   * g_menu_item_get_attribute_value:
     766   * @menu_item: a #GMenuItem
     767   * @attribute: the attribute name to query
     768   * @expected_type: (nullable): the expected type of the attribute
     769   *
     770   * Queries the named @attribute on @menu_item.
     771   *
     772   * If @expected_type is specified and the attribute does not have this
     773   * type, %NULL is returned.  %NULL is also returned if the attribute
     774   * simply does not exist.
     775   *
     776   * Returns: (nullable) (transfer full): the attribute value, or %NULL
     777   *
     778   * Since: 2.34
     779   */
     780  GVariant *
     781  g_menu_item_get_attribute_value (GMenuItem          *menu_item,
     782                                   const gchar        *attribute,
     783                                   const GVariantType *expected_type)
     784  {
     785    GVariant *value;
     786  
     787    g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
     788    g_return_val_if_fail (attribute != NULL, NULL);
     789  
     790    value = g_hash_table_lookup (menu_item->attributes, attribute);
     791  
     792    if (value != NULL)
     793      {
     794        if (expected_type == NULL || g_variant_is_of_type (value, expected_type))
     795          g_variant_ref (value);
     796        else
     797          value = NULL;
     798      }
     799  
     800    return value;
     801  }
     802  
     803  /**
     804   * g_menu_item_get_attribute:
     805   * @menu_item: a #GMenuItem
     806   * @attribute: the attribute name to query
     807   * @format_string: a #GVariant format string
     808   * @...: positional parameters, as per @format_string
     809   *
     810   * Queries the named @attribute on @menu_item.
     811   *
     812   * If the attribute exists and matches the #GVariantType corresponding
     813   * to @format_string then @format_string is used to deconstruct the
     814   * value into the positional parameters and %TRUE is returned.
     815   *
     816   * If the attribute does not exist, or it does exist but has the wrong
     817   * type, then the positional parameters are ignored and %FALSE is
     818   * returned.
     819   *
     820   * Returns: %TRUE if the named attribute was found with the expected
     821   *     type
     822   *
     823   * Since: 2.34
     824   */
     825  gboolean
     826  g_menu_item_get_attribute (GMenuItem   *menu_item,
     827                             const gchar *attribute,
     828                             const gchar *format_string,
     829                             ...)
     830  {
     831    GVariant *value;
     832    va_list ap;
     833  
     834    g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), FALSE);
     835    g_return_val_if_fail (attribute != NULL, FALSE);
     836    g_return_val_if_fail (format_string != NULL, FALSE);
     837  
     838    value = g_hash_table_lookup (menu_item->attributes, attribute);
     839  
     840    if (value == NULL)
     841      return FALSE;
     842  
     843    if (!g_variant_check_format_string (value, format_string, FALSE))
     844      return FALSE;
     845  
     846    va_start (ap, format_string);
     847    g_variant_get_va (value, format_string, NULL, &ap);
     848    va_end (ap);
     849  
     850    return TRUE;
     851  }
     852  
     853  /**
     854   * g_menu_item_get_link:
     855   * @menu_item: a #GMenuItem
     856   * @link: the link name to query
     857   *
     858   * Queries the named @link on @menu_item.
     859   *
     860   * Returns: (nullable) (transfer full): the link, or %NULL
     861   *
     862   * Since: 2.34
     863   */
     864  GMenuModel *
     865  g_menu_item_get_link (GMenuItem   *menu_item,
     866                        const gchar *link)
     867  {
     868    GMenuModel *model;
     869  
     870    g_return_val_if_fail (G_IS_MENU_ITEM (menu_item), NULL);
     871    g_return_val_if_fail (link != NULL, NULL);
     872    g_return_val_if_fail (valid_attribute_name (link), NULL);
     873  
     874    model = g_hash_table_lookup (menu_item->links, link);
     875  
     876    if (model)
     877      g_object_ref (model);
     878  
     879    return model;
     880  }
     881  
     882  /**
     883   * g_menu_item_set_label:
     884   * @menu_item: a #GMenuItem
     885   * @label: (nullable): the label to set, or %NULL to unset
     886   *
     887   * Sets or unsets the "label" attribute of @menu_item.
     888   *
     889   * If @label is non-%NULL it is used as the label for the menu item.  If
     890   * it is %NULL then the label attribute is unset.
     891   *
     892   * Since: 2.32
     893   */
     894  void
     895  g_menu_item_set_label (GMenuItem   *menu_item,
     896                         const gchar *label)
     897  {
     898    GVariant *value;
     899  
     900    if (label != NULL)
     901      value = g_variant_new_string (label);
     902    else
     903      value = NULL;
     904  
     905    g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_LABEL, value);
     906  }
     907  
     908  /**
     909   * g_menu_item_set_submenu:
     910   * @menu_item: a #GMenuItem
     911   * @submenu: (nullable): a #GMenuModel, or %NULL
     912   *
     913   * Sets or unsets the "submenu" link of @menu_item to @submenu.
     914   *
     915   * If @submenu is non-%NULL, it is linked to.  If it is %NULL then the
     916   * link is unset.
     917   *
     918   * The effect of having one menu appear as a submenu of another is
     919   * exactly as it sounds.
     920   *
     921   * Since: 2.32
     922   */
     923  void
     924  g_menu_item_set_submenu (GMenuItem  *menu_item,
     925                           GMenuModel *submenu)
     926  {
     927    g_menu_item_set_link (menu_item, G_MENU_LINK_SUBMENU, submenu);
     928  }
     929  
     930  /**
     931   * g_menu_item_set_section:
     932   * @menu_item: a #GMenuItem
     933   * @section: (nullable): a #GMenuModel, or %NULL
     934   *
     935   * Sets or unsets the "section" link of @menu_item to @section.
     936   *
     937   * The effect of having one menu appear as a section of another is
     938   * exactly as it sounds: the items from @section become a direct part of
     939   * the menu that @menu_item is added to.  See g_menu_item_new_section()
     940   * for more information about what it means for a menu item to be a
     941   * section.
     942   *
     943   * Since: 2.32
     944   */
     945  void
     946  g_menu_item_set_section (GMenuItem  *menu_item,
     947                           GMenuModel *section)
     948  {
     949    g_menu_item_set_link (menu_item, G_MENU_LINK_SECTION, section);
     950  }
     951  
     952  /**
     953   * g_menu_item_set_action_and_target_value:
     954   * @menu_item: a #GMenuItem
     955   * @action: (nullable): the name of the action for this item
     956   * @target_value: (nullable): a #GVariant to use as the action target
     957   *
     958   * Sets or unsets the "action" and "target" attributes of @menu_item.
     959   *
     960   * If @action is %NULL then both the "action" and "target" attributes
     961   * are unset (and @target_value is ignored).
     962   *
     963   * If @action is non-%NULL then the "action" attribute is set.  The
     964   * "target" attribute is then set to the value of @target_value if it is
     965   * non-%NULL or unset otherwise.
     966   *
     967   * Normal menu items (ie: not submenu, section or other custom item
     968   * types) are expected to have the "action" attribute set to identify
     969   * the action that they are associated with.  The state type of the
     970   * action help to determine the disposition of the menu item.  See
     971   * #GAction and #GActionGroup for an overview of actions.
     972   *
     973   * In general, clicking on the menu item will result in activation of
     974   * the named action with the "target" attribute given as the parameter
     975   * to the action invocation.  If the "target" attribute is not set then
     976   * the action is invoked with no parameter.
     977   *
     978   * If the action has no state then the menu item is usually drawn as a
     979   * plain menu item (ie: with no additional decoration).
     980   *
     981   * If the action has a boolean state then the menu item is usually drawn
     982   * as a toggle menu item (ie: with a checkmark or equivalent
     983   * indication).  The item should be marked as 'toggled' or 'checked'
     984   * when the boolean state is %TRUE.
     985   *
     986   * If the action has a string state then the menu item is usually drawn
     987   * as a radio menu item (ie: with a radio bullet or equivalent
     988   * indication).  The item should be marked as 'selected' when the string
     989   * state is equal to the value of the @target property.
     990   *
     991   * See g_menu_item_set_action_and_target() or
     992   * g_menu_item_set_detailed_action() for two equivalent calls that are
     993   * probably more convenient for most uses.
     994   *
     995   * Since: 2.32
     996   */
     997  void
     998  g_menu_item_set_action_and_target_value (GMenuItem   *menu_item,
     999                                           const gchar *action,
    1000                                           GVariant    *target_value)
    1001  {
    1002    GVariant *action_value;
    1003  
    1004    if (action != NULL)
    1005      {
    1006        action_value = g_variant_new_string (action);
    1007      }
    1008    else
    1009      {
    1010        action_value = NULL;
    1011        target_value = NULL;
    1012      }
    1013  
    1014    g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ACTION, action_value);
    1015    g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_TARGET, target_value);
    1016  }
    1017  
    1018  /**
    1019   * g_menu_item_set_action_and_target:
    1020   * @menu_item: a #GMenuItem
    1021   * @action: (nullable): the name of the action for this item
    1022   * @format_string: (nullable): a GVariant format string
    1023   * @...: positional parameters, as per @format_string
    1024   *
    1025   * Sets or unsets the "action" and "target" attributes of @menu_item.
    1026   *
    1027   * If @action is %NULL then both the "action" and "target" attributes
    1028   * are unset (and @format_string is ignored along with the positional
    1029   * parameters).
    1030   *
    1031   * If @action is non-%NULL then the "action" attribute is set.
    1032   * @format_string is then inspected.  If it is non-%NULL then the proper
    1033   * position parameters are collected to create a #GVariant instance to
    1034   * use as the target value.  If it is %NULL then the positional
    1035   * parameters are ignored and the "target" attribute is unset.
    1036   *
    1037   * See also g_menu_item_set_action_and_target_value() for an equivalent
    1038   * call that directly accepts a #GVariant.  See
    1039   * g_menu_item_set_detailed_action() for a more convenient version that
    1040   * works with string-typed targets.
    1041   *
    1042   * See also g_menu_item_set_action_and_target_value() for a
    1043   * description of the semantics of the action and target attributes.
    1044   *
    1045   * Since: 2.32
    1046   */
    1047  void
    1048  g_menu_item_set_action_and_target (GMenuItem   *menu_item,
    1049                                     const gchar *action,
    1050                                     const gchar *format_string,
    1051                                     ...)
    1052  {
    1053    GVariant *value;
    1054  
    1055    if (format_string != NULL)
    1056      {
    1057        va_list ap;
    1058  
    1059        va_start (ap, format_string);
    1060        value = g_variant_new_va (format_string, NULL, &ap);
    1061        va_end (ap);
    1062      }
    1063    else
    1064      value = NULL;
    1065  
    1066    g_menu_item_set_action_and_target_value (menu_item, action, value);
    1067  }
    1068  
    1069  /**
    1070   * g_menu_item_set_detailed_action:
    1071   * @menu_item: a #GMenuItem
    1072   * @detailed_action: the "detailed" action string
    1073   *
    1074   * Sets the "action" and possibly the "target" attribute of @menu_item.
    1075   *
    1076   * The format of @detailed_action is the same format parsed by
    1077   * g_action_parse_detailed_name().
    1078   *
    1079   * See g_menu_item_set_action_and_target() or
    1080   * g_menu_item_set_action_and_target_value() for more flexible (but
    1081   * slightly less convenient) alternatives.
    1082   *
    1083   * See also g_menu_item_set_action_and_target_value() for a description of
    1084   * the semantics of the action and target attributes.
    1085   *
    1086   * Since: 2.32
    1087   */
    1088  void
    1089  g_menu_item_set_detailed_action (GMenuItem   *menu_item,
    1090                                   const gchar *detailed_action)
    1091  {
    1092    GError *error = NULL;
    1093    GVariant *target;
    1094    gchar *name;
    1095  
    1096    if (!g_action_parse_detailed_name (detailed_action, &name, &target, &error))
    1097      g_error ("g_menu_item_set_detailed_action: %s", error->message);
    1098  
    1099    g_menu_item_set_action_and_target_value (menu_item, name, target);
    1100    if (target)
    1101      g_variant_unref (target);
    1102    g_free (name);
    1103  }
    1104  
    1105  /**
    1106   * g_menu_item_new:
    1107   * @label: (nullable): the section label, or %NULL
    1108   * @detailed_action: (nullable): the detailed action string, or %NULL
    1109   *
    1110   * Creates a new #GMenuItem.
    1111   *
    1112   * If @label is non-%NULL it is used to set the "label" attribute of the
    1113   * new item.
    1114   *
    1115   * If @detailed_action is non-%NULL it is used to set the "action" and
    1116   * possibly the "target" attribute of the new item.  See
    1117   * g_menu_item_set_detailed_action() for more information.
    1118   *
    1119   * Returns: a new #GMenuItem
    1120   *
    1121   * Since: 2.32
    1122   */
    1123  GMenuItem *
    1124  g_menu_item_new (const gchar *label,
    1125                   const gchar *detailed_action)
    1126  {
    1127    GMenuItem *menu_item;
    1128  
    1129    menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
    1130  
    1131    if (label != NULL)
    1132      g_menu_item_set_label (menu_item, label);
    1133  
    1134    if (detailed_action != NULL)
    1135      g_menu_item_set_detailed_action (menu_item, detailed_action);
    1136  
    1137    return menu_item;
    1138  }
    1139  
    1140  /**
    1141   * g_menu_item_new_submenu:
    1142   * @label: (nullable): the section label, or %NULL
    1143   * @submenu: a #GMenuModel with the items of the submenu
    1144   *
    1145   * Creates a new #GMenuItem representing a submenu.
    1146   *
    1147   * This is a convenience API around g_menu_item_new() and
    1148   * g_menu_item_set_submenu().
    1149   *
    1150   * Returns: a new #GMenuItem
    1151   *
    1152   * Since: 2.32
    1153   */
    1154  GMenuItem *
    1155  g_menu_item_new_submenu (const gchar *label,
    1156                           GMenuModel  *submenu)
    1157  {
    1158    GMenuItem *menu_item;
    1159  
    1160    menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
    1161  
    1162    if (label != NULL)
    1163      g_menu_item_set_label (menu_item, label);
    1164  
    1165    g_menu_item_set_submenu (menu_item, submenu);
    1166  
    1167    return menu_item;
    1168  }
    1169  
    1170  /**
    1171   * g_menu_item_new_section:
    1172   * @label: (nullable): the section label, or %NULL
    1173   * @section: a #GMenuModel with the items of the section
    1174   *
    1175   * Creates a new #GMenuItem representing a section.
    1176   *
    1177   * This is a convenience API around g_menu_item_new() and
    1178   * g_menu_item_set_section().
    1179   *
    1180   * The effect of having one menu appear as a section of another is
    1181   * exactly as it sounds: the items from @section become a direct part of
    1182   * the menu that @menu_item is added to.
    1183   *
    1184   * Visual separation is typically displayed between two non-empty
    1185   * sections.  If @label is non-%NULL then it will be encorporated into
    1186   * this visual indication.  This allows for labeled subsections of a
    1187   * menu.
    1188   *
    1189   * As a simple example, consider a typical "Edit" menu from a simple
    1190   * program.  It probably contains an "Undo" and "Redo" item, followed by
    1191   * a separator, followed by "Cut", "Copy" and "Paste".
    1192   *
    1193   * This would be accomplished by creating three #GMenu instances.  The
    1194   * first would be populated with the "Undo" and "Redo" items, and the
    1195   * second with the "Cut", "Copy" and "Paste" items.  The first and
    1196   * second menus would then be added as submenus of the third.  In XML
    1197   * format, this would look something like the following:
    1198   * |[
    1199   * <menu id='edit-menu'>
    1200   *   <section>
    1201   *     <item label='Undo'/>
    1202   *     <item label='Redo'/>
    1203   *   </section>
    1204   *   <section>
    1205   *     <item label='Cut'/>
    1206   *     <item label='Copy'/>
    1207   *     <item label='Paste'/>
    1208   *   </section>
    1209   * </menu>
    1210   * ]|
    1211   *
    1212   * The following example is exactly equivalent.  It is more illustrative
    1213   * of the exact relationship between the menus and items (keeping in
    1214   * mind that the 'link' element defines a new menu that is linked to the
    1215   * containing one).  The style of the second example is more verbose and
    1216   * difficult to read (and therefore not recommended except for the
    1217   * purpose of understanding what is really going on).
    1218   * |[
    1219   * <menu id='edit-menu'>
    1220   *   <item>
    1221   *     <link name='section'>
    1222   *       <item label='Undo'/>
    1223   *       <item label='Redo'/>
    1224   *     </link>
    1225   *   </item>
    1226   *   <item>
    1227   *     <link name='section'>
    1228   *       <item label='Cut'/>
    1229   *       <item label='Copy'/>
    1230   *       <item label='Paste'/>
    1231   *     </link>
    1232   *   </item>
    1233   * </menu>
    1234   * ]|
    1235   *
    1236   * Returns: a new #GMenuItem
    1237   *
    1238   * Since: 2.32
    1239   */
    1240  GMenuItem *
    1241  g_menu_item_new_section (const gchar *label,
    1242                           GMenuModel  *section)
    1243  {
    1244    GMenuItem *menu_item;
    1245  
    1246    menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
    1247  
    1248    if (label != NULL)
    1249      g_menu_item_set_label (menu_item, label);
    1250  
    1251    g_menu_item_set_section (menu_item, section);
    1252  
    1253    return menu_item;
    1254  }
    1255  
    1256  /**
    1257   * g_menu_item_new_from_model:
    1258   * @model: a #GMenuModel
    1259   * @item_index: the index of an item in @model
    1260   *
    1261   * Creates a #GMenuItem as an exact copy of an existing menu item in a
    1262   * #GMenuModel.
    1263   *
    1264   * @item_index must be valid (ie: be sure to call
    1265   * g_menu_model_get_n_items() first).
    1266   *
    1267   * Returns: a new #GMenuItem.
    1268   *
    1269   * Since: 2.34
    1270   */
    1271  GMenuItem *
    1272  g_menu_item_new_from_model (GMenuModel *model,
    1273                              gint        item_index)
    1274  {
    1275    GMenuModelClass *class = G_MENU_MODEL_GET_CLASS (model);
    1276    GMenuItem *menu_item;
    1277  
    1278    menu_item = g_object_new (G_TYPE_MENU_ITEM, NULL);
    1279  
    1280    /* With some trickery we can be pretty efficient.
    1281     *
    1282     * A GMenuModel must either implement iterate_item_attributes() or
    1283     * get_item_attributes().  If it implements get_item_attributes() then
    1284     * we are in luck -- we can just take a reference on the returned
    1285     * hashtable and mark ourselves as copy-on-write.
    1286     *
    1287     * In the case that the model is based on get_item_attributes (which
    1288     * is the case for both GMenu and GDBusMenuModel) then this is
    1289     * basically just g_hash_table_ref().
    1290     */
    1291    if (class->get_item_attributes)
    1292      {
    1293        GHashTable *attributes = NULL;
    1294  
    1295        class->get_item_attributes (model, item_index, &attributes);
    1296        if (attributes)
    1297          {
    1298            g_hash_table_unref (menu_item->attributes);
    1299            menu_item->attributes = attributes;
    1300            menu_item->cow = TRUE;
    1301          }
    1302      }
    1303    else
    1304      {
    1305        GMenuAttributeIter *iter;
    1306        const gchar *attribute;
    1307        GVariant *value;
    1308  
    1309        iter = g_menu_model_iterate_item_attributes (model, item_index);
    1310        while (g_menu_attribute_iter_get_next (iter, &attribute, &value))
    1311          g_hash_table_insert (menu_item->attributes, g_strdup (attribute), value);
    1312        g_object_unref (iter);
    1313      }
    1314  
    1315    /* Same story for the links... */
    1316    if (class->get_item_links)
    1317      {
    1318        GHashTable *links = NULL;
    1319  
    1320        class->get_item_links (model, item_index, &links);
    1321        if (links)
    1322          {
    1323            g_hash_table_unref (menu_item->links);
    1324            menu_item->links = links;
    1325            menu_item->cow = TRUE;
    1326          }
    1327      }
    1328    else
    1329      {
    1330        GMenuLinkIter *iter;
    1331        const gchar *link;
    1332        GMenuModel *value;
    1333  
    1334        iter = g_menu_model_iterate_item_links (model, item_index);
    1335        while (g_menu_link_iter_get_next (iter, &link, &value))
    1336          g_hash_table_insert (menu_item->links, g_strdup (link), value);
    1337        g_object_unref (iter);
    1338      }
    1339  
    1340    return menu_item;
    1341  }
    1342  
    1343  /**
    1344   * g_menu_item_set_icon:
    1345   * @menu_item: a #GMenuItem
    1346   * @icon: a #GIcon, or %NULL
    1347   *
    1348   * Sets (or unsets) the icon on @menu_item.
    1349   *
    1350   * This call is the same as calling g_icon_serialize() and using the
    1351   * result as the value to g_menu_item_set_attribute_value() for
    1352   * %G_MENU_ATTRIBUTE_ICON.
    1353   *
    1354   * This API is only intended for use with "noun" menu items; things like
    1355   * bookmarks or applications in an "Open With" menu.  Don't use it on
    1356   * menu items corresponding to verbs (eg: stock icons for 'Save' or
    1357   * 'Quit').
    1358   *
    1359   * If @icon is %NULL then the icon is unset.
    1360   *
    1361   * Since: 2.38
    1362   **/
    1363  void
    1364  g_menu_item_set_icon (GMenuItem *menu_item,
    1365                        GIcon     *icon)
    1366  {
    1367    GVariant *value;
    1368  
    1369    g_return_if_fail (G_IS_MENU_ITEM (menu_item));
    1370    g_return_if_fail (icon == NULL || G_IS_ICON (icon));
    1371  
    1372    if (icon != NULL)
    1373      value = g_icon_serialize (icon);
    1374    else
    1375      value = NULL;
    1376  
    1377    g_menu_item_set_attribute_value (menu_item, G_MENU_ATTRIBUTE_ICON, value);
    1378    if (value)
    1379      g_variant_unref (value);
    1380  }