1313 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1313 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/* PIKA - Photo and Image Kooker Application
							 | 
						||
| 
								 | 
							
								 * a rebranding of The GNU Image Manipulation Program (created with heckimp)
							 | 
						||
| 
								 | 
							
								 * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Original copyright, applying to most contents (license remains unchanged): 
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * pikamenu_model.c
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 2023 Jehan
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program is free software: you can redistribute it and/or modify
							 | 
						||
| 
								 | 
							
								 * it under the terms of the GNU General Public License as published by
							 | 
						||
| 
								 | 
							
								 * the Free Software Foundation; either version 3 of the License, or
							 | 
						||
| 
								 | 
							
								 * (at your option) any later version.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program 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 General Public License for more details.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * You should have received a copy of the GNU General Public License
							 | 
						||
| 
								 | 
							
								 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "config.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <gegl.h>
							 | 
						||
| 
								 | 
							
								#include <gtk/gtk.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "widgets-types.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "libpikabase/pikabase.h"
							 | 
						||
| 
								 | 
							
								#include "libpikacolor/pikacolor.h"
							 | 
						||
| 
								 | 
							
								#include "libpikawidgets/pikawidgets.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "core/pika.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "pikaaction.h"
							 | 
						||
| 
								 | 
							
								#include "pikaactiongroup.h"
							 | 
						||
| 
								 | 
							
								#include "pikamenumodel.h"
							 | 
						||
| 
								 | 
							
								#include "pikamenushell.h"
							 | 
						||
| 
								 | 
							
								#include "pikaradioaction.h"
							 | 
						||
| 
								 | 
							
								#include "pikauimanager.h"
							 | 
						||
| 
								 | 
							
								#include "pikawidgets-utils.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * PikaMenuModel:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * PikaMenuModel implements GMenuModel. We initialize an object from another
							 | 
						||
| 
								 | 
							
								 * GMenuModel (usually a GMenu), auto-fill with various data from the
							 | 
						||
| 
								 | 
							
								 * PikaAction, when they are not in GAction API, e.g. labels, but action
							 | 
						||
| 
								 | 
							
								 * visibility.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The object will also synchronize automatically with changes from the actions,
							 | 
						||
| 
								 | 
							
								 * but also PikaUIManager for dynamic contents and will trigger an
							 | 
						||
| 
								 | 
							
								 * "items-changed" when necessary. This allows for such variant to be used in
							 | 
						||
| 
								 | 
							
								 * GTK API which has no knowledge of PikaAction or PikaUIManager enhancements.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PROP_0,
							 | 
						||
| 
								 | 
							
								  PROP_MANAGER,
							 | 
						||
| 
								 | 
							
								  PROP_MODEL,
							 | 
						||
| 
								 | 
							
								  PROP_PATH,
							 | 
						||
| 
								 | 
							
								  PROP_IS_SECTION,
							 | 
						||
| 
								 | 
							
								  PROP_TITLE,
							 | 
						||
| 
								 | 
							
								  PROP_COLOR,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct _PikaMenuModelPrivate
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager;
							 | 
						||
| 
								 | 
							
								  GMenuModel    *model;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gchar         *path;
							 | 
						||
| 
								 | 
							
								  gboolean       is_section;
							 | 
						||
| 
								 | 
							
								  /* If this PikaMenuModel represents a submenu for a bigger menu, this object
							 | 
						||
| 
								 | 
							
								   * will not be NULL.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  GMenuItem     *submenu_item;
							 | 
						||
| 
								 | 
							
								  PikaRGB       *submenu_color;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  GList         *items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  GHashTable    *named_sections;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_finalize                 (GObject             *object);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_get_property             (GObject             *object,
							 | 
						||
| 
								 | 
							
								                                                              guint                property_id,
							 | 
						||
| 
								 | 
							
								                                                              GValue              *value,
							 | 
						||
| 
								 | 
							
								                                                              GParamSpec          *pspec);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_set_property             (GObject             *object,
							 | 
						||
| 
								 | 
							
								                                                              guint                property_id,
							 | 
						||
| 
								 | 
							
								                                                              const GValue        *value,
							 | 
						||
| 
								 | 
							
								                                                              GParamSpec          *pspec);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GVariant   * pika_menu_model_get_item_attribute_value (GMenuModel          *model,
							 | 
						||
| 
								 | 
							
								                                                              gint                 item_index,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *attribute,
							 | 
						||
| 
								 | 
							
								                                                              const GVariantType  *expected_type);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_get_item_attributes      (GMenuModel          *model,
							 | 
						||
| 
								 | 
							
								                                                              gint                 item_index,
							 | 
						||
| 
								 | 
							
								                                                              GHashTable         **attributes);
							 | 
						||
| 
								 | 
							
								static GMenuModel * pika_menu_model_get_item_link            (GMenuModel          *model,
							 | 
						||
| 
								 | 
							
								                                                              gint                 item_index,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *link);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_get_item_links           (GMenuModel          *model,
							 | 
						||
| 
								 | 
							
								                                                              gint                 item_index,
							 | 
						||
| 
								 | 
							
								                                                              GHashTable         **links);
							 | 
						||
| 
								 | 
							
								static gint         pika_menu_model_get_n_items              (GMenuModel          *model);
							 | 
						||
| 
								 | 
							
								static gint         pika_menu_model_get_position             (PikaMenuModel       *model,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *action_name,
							 | 
						||
| 
								 | 
							
								                                                              gboolean            *visible);;
							 | 
						||
| 
								 | 
							
								static gboolean     pika_menu_model_is_mutable               (GMenuModel          *model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_initialize               (PikaMenuModel       *model,
							 | 
						||
| 
								 | 
							
								                                                              GMenuModel          *gmodel);
							 | 
						||
| 
								 | 
							
								static gchar      * pika_menu_model_handles_subpath          (PikaMenuModel       *model,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *canonical_path,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *mnemonic_path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GMenuItem  * pika_menu_model_get_item                 (PikaMenuModel       *model,
							 | 
						||
| 
								 | 
							
								                                                              gint                 idx);
							 | 
						||
| 
								 | 
							
								static GMenuItem  * pika_menu_model_get_menu_item_rec        (PikaMenuModel       *model,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *path,
							 | 
						||
| 
								 | 
							
								                                                              PikaMenuModel      **menu,
							 | 
						||
| 
								 | 
							
								                                                              GMenuItem           *item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_notify_group_label       (PikaRadioAction     *action,
							 | 
						||
| 
								 | 
							
								                                                              const GParamSpec    *pspec,
							 | 
						||
| 
								 | 
							
								                                                              GMenuItem           *item);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_action_notify_visible    (PikaAction          *action,
							 | 
						||
| 
								 | 
							
								                                                              GParamSpec          *pspec,
							 | 
						||
| 
								 | 
							
								                                                              PikaMenuModel       *model);
							 | 
						||
| 
								 | 
							
								static void         pika_menu_model_action_notify_label      (PikaAction          *action,
							 | 
						||
| 
								 | 
							
								                                                              GParamSpec          *pspec,
							 | 
						||
| 
								 | 
							
								                                                              GMenuItem           *item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gboolean     pika_menu_model_ui_added                 (PikaUIManager       *manager,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *path,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *action_name,
							 | 
						||
| 
								 | 
							
								                                                              gboolean             top,
							 | 
						||
| 
								 | 
							
								                                                              PikaMenuModel       *model);
							 | 
						||
| 
								 | 
							
								static gboolean     pika_menu_model_ui_removed               (PikaUIManager       *manager,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *path,
							 | 
						||
| 
								 | 
							
								                                                              const gchar         *action_name,
							 | 
						||
| 
								 | 
							
								                                                              PikaMenuModel       *model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								G_DEFINE_TYPE_WITH_CODE (PikaMenuModel, pika_menu_model, G_TYPE_MENU_MODEL,
							 | 
						||
| 
								 | 
							
								                         G_ADD_PRIVATE (PikaMenuModel))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define parent_class pika_menu_model_parent_class
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*  Class functions  */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_class_init (PikaMenuModelClass *klass)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GObjectClass    *object_class = G_OBJECT_CLASS (klass);
							 | 
						||
| 
								 | 
							
								  GMenuModelClass *model_class  = G_MENU_MODEL_CLASS (klass);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  object_class->finalize     = pika_menu_model_finalize;
							 | 
						||
| 
								 | 
							
								  object_class->get_property = pika_menu_model_get_property;
							 | 
						||
| 
								 | 
							
								  object_class->set_property = pika_menu_model_set_property;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model_class->get_item_attribute_value = pika_menu_model_get_item_attribute_value;
							 | 
						||
| 
								 | 
							
								  model_class->get_item_attributes      = pika_menu_model_get_item_attributes;
							 | 
						||
| 
								 | 
							
								  model_class->get_item_link            = pika_menu_model_get_item_link;
							 | 
						||
| 
								 | 
							
								  model_class->get_item_links           = pika_menu_model_get_item_links;
							 | 
						||
| 
								 | 
							
								  model_class->get_n_items              = pika_menu_model_get_n_items;
							 | 
						||
| 
								 | 
							
								  model_class->is_mutable               = pika_menu_model_is_mutable;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_MANAGER,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_object ("manager",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_TYPE_UI_MANAGER,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_CONSTRUCT));
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_MODEL,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_object ("model",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        G_TYPE_MENU_MODEL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE));
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_PATH,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_string ("path",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_WRITABLE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_CONSTRUCT_ONLY));
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_IS_SECTION,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_boolean ("section",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL, FALSE,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_WRITABLE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_CONSTRUCT_ONLY));
							 | 
						||
| 
								 | 
							
								  /* Titles are only relevant if the model is that of a submenu. */
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_TITLE,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_string ("title",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_EXPLICIT_NOTIFY));
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_COLOR,
							 | 
						||
| 
								 | 
							
								                                   pika_param_spec_rgb ("color",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        TRUE, &(PikaRGB) {},
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_EXPLICIT_NOTIFY));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_init (PikaMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  model->priv = pika_menu_model_get_instance_private (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model->priv->items         = NULL;
							 | 
						||
| 
								 | 
							
								  model->priv->path          = NULL;
							 | 
						||
| 
								 | 
							
								  model->priv->is_section    = FALSE;
							 | 
						||
| 
								 | 
							
								  model->priv->submenu_item  = NULL;
							 | 
						||
| 
								 | 
							
								  model->priv->submenu_color = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model->priv->named_sections = g_hash_table_new_full (g_str_hash, g_str_equal,
							 | 
						||
| 
								 | 
							
								                                                       g_free, NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_finalize (GObject *object)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model = PIKA_MENU_MODEL (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_clear_weak_pointer (&model->priv->manager);
							 | 
						||
| 
								 | 
							
								  g_clear_object (&model->priv->model);
							 | 
						||
| 
								 | 
							
								  g_list_free_full (model->priv->items, g_object_unref);
							 | 
						||
| 
								 | 
							
								  g_free (model->priv->path);
							 | 
						||
| 
								 | 
							
								  g_free (model->priv->submenu_color);
							 | 
						||
| 
								 | 
							
								  g_hash_table_destroy (model->priv->named_sections);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  G_OBJECT_CLASS (parent_class)->finalize (object);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_property (GObject    *object,
							 | 
						||
| 
								 | 
							
								                              guint       property_id,
							 | 
						||
| 
								 | 
							
								                              GValue     *value,
							 | 
						||
| 
								 | 
							
								                              GParamSpec *pspec)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model = PIKA_MENU_MODEL (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch (property_id)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								    case PROP_MANAGER:
							 | 
						||
| 
								 | 
							
								      g_value_set_object (value, model->priv->manager);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_MODEL:
							 | 
						||
| 
								 | 
							
								      g_value_set_object (value, model->priv->model);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_TITLE:
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gchar *title;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_menu_item_get_attribute (model->priv->submenu_item, G_MENU_ATTRIBUTE_LABEL, "s", &title);
							 | 
						||
| 
								 | 
							
								          g_value_set_string (value, title);
							 | 
						||
| 
								 | 
							
								          g_free (title);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_COLOR:
							 | 
						||
| 
								 | 
							
								      g_value_set_boxed (value, model->priv->submenu_color);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_menu_model_set_property (GObject      *object,
							 | 
						||
| 
								 | 
							
								                              guint         property_id,
							 | 
						||
| 
								 | 
							
								                              const GValue *value,
							 | 
						||
| 
								 | 
							
								                              GParamSpec   *pspec)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model = PIKA_MENU_MODEL (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch (property_id)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								    case PROP_MANAGER:
							 | 
						||
| 
								 | 
							
								      g_set_weak_pointer (&model->priv->manager, g_value_get_object (value));
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_MODEL:
							 | 
						||
| 
								 | 
							
								      model->priv->model = g_value_dup_object (value);
							 | 
						||
| 
								 | 
							
								      pika_menu_model_initialize (model, model->priv->model);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_PATH:
							 | 
						||
| 
								 | 
							
								      model->priv->path = g_value_dup_string (value);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_IS_SECTION:
							 | 
						||
| 
								 | 
							
								      model->priv->is_section = g_value_get_boolean (value);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_TITLE:
							 | 
						||
| 
								 | 
							
								      pika_menu_model_set_title (model, model->priv->path,
							 | 
						||
| 
								 | 
							
								                                 g_value_get_string (value));
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case PROP_COLOR:
							 | 
						||
| 
								 | 
							
								      pika_menu_model_set_color (model, model->priv->path,
							 | 
						||
| 
								 | 
							
								                                 g_value_get_boxed (value));
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GVariant*
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_item_attribute_value (GMenuModel         *model,
							 | 
						||
| 
								 | 
							
								                                          gint                item_index,
							 | 
						||
| 
								 | 
							
								                                          const gchar        *attribute,
							 | 
						||
| 
								 | 
							
								                                          const GVariantType *expected_type)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = pika_menu_model_get_item (m, item_index);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return g_menu_item_get_attribute_value (item, attribute, expected_type);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_item_attributes (GMenuModel  *model,
							 | 
						||
| 
								 | 
							
								                                     gint         item_index,
							 | 
						||
| 
								 | 
							
								                                     GHashTable **attributes)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								  GVariant      *value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = pika_menu_model_get_item (m, item_index);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  *attributes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
							 | 
						||
| 
								 | 
							
								                                       (GDestroyNotify) g_variant_unref);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_LABEL, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_LABEL, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ACTION, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ICON, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ICON, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_LINK_SUBMENU, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_LINK_SUBMENU, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_LINK_SECTION, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_LINK_SECTION, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_TARGET, NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_TARGET, value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  value = g_menu_item_get_attribute_value (item, "hidden-when", NULL);
							 | 
						||
| 
								 | 
							
								  if (value)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*attributes, "hidden-when", value);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GMenuModel*
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_item_link (GMenuModel  *model,
							 | 
						||
| 
								 | 
							
								                               gint         item_index,
							 | 
						||
| 
								 | 
							
								                               const gchar *link)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = pika_menu_model_get_item (m, item_index);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return g_menu_item_get_link (item, link);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_item_links (GMenuModel  *model,
							 | 
						||
| 
								 | 
							
								                                gint         item_index,
							 | 
						||
| 
								 | 
							
								                                GHashTable **links)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								  GMenuModel    *subsection;
							 | 
						||
| 
								 | 
							
								  GMenuModel    *submenu;
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  *links = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
							 | 
						||
| 
								 | 
							
								                                  (GDestroyNotify) g_object_unref);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item       = pika_menu_model_get_item (m, item_index);
							 | 
						||
| 
								 | 
							
								  subsection = g_menu_item_get_link (item, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								  submenu    = g_menu_item_get_link (item, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (subsection)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*links, G_MENU_LINK_SECTION, g_object_ref (subsection));
							 | 
						||
| 
								 | 
							
								  if (submenu)
							 | 
						||
| 
								 | 
							
								    g_hash_table_insert (*links, G_MENU_LINK_SUBMENU, g_object_ref (submenu));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								  g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gint
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_n_items (GMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return pika_menu_model_get_position (m, NULL, NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* This function has 2 usage:
							 | 
						||
| 
								 | 
							
								 * - Either you call it with @action_name == NULL, then it returns the
							 | 
						||
| 
								 | 
							
								 *   total number of visible items in this model.
							 | 
						||
| 
								 | 
							
								 * - Or it returns the position of @action_name and its visible state.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								static gint
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_position (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                              const gchar   *action_name,
							 | 
						||
| 
								 | 
							
								                              gboolean      *visible)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *iter;
							 | 
						||
| 
								 | 
							
								  gint   len = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (iter = model->priv->items; iter; iter = iter->next)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GMenuModel  *subsection;
							 | 
						||
| 
								 | 
							
								      GMenuModel  *submenu;
							 | 
						||
| 
								 | 
							
								      const gchar *cur_action_name = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								      submenu    = g_menu_item_get_link (iter->data, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (subsection || submenu)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          len++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      /* Count neither placeholders (items with no action name), nor invisible
							 | 
						||
| 
								 | 
							
								       * actions.
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      else if (g_menu_item_get_attribute (iter->data,
							 | 
						||
| 
								 | 
							
								                                          G_MENU_ATTRIBUTE_ACTION,
							 | 
						||
| 
								 | 
							
								                                          "&s", &cur_action_name))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaAction  *cur_action       = NULL;
							 | 
						||
| 
								 | 
							
								          const gchar *real_action_name = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (cur_action_name != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              real_action_name = strstr (cur_action_name, ".");
							 | 
						||
| 
								 | 
							
								              if (real_action_name != NULL)
							 | 
						||
| 
								 | 
							
								                real_action_name++;
							 | 
						||
| 
								 | 
							
								              else
							 | 
						||
| 
								 | 
							
								                real_action_name = cur_action_name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              cur_action = pika_ui_manager_find_action (model->priv->manager,
							 | 
						||
| 
								 | 
							
								                                                        NULL, cur_action_name);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (cur_action_name != NULL &&
							 | 
						||
| 
								 | 
							
								              g_strcmp0 (action_name, real_action_name) == 0)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              if (visible)
							 | 
						||
| 
								 | 
							
								                {
							 | 
						||
| 
								 | 
							
								                  if (cur_action != NULL)
							 | 
						||
| 
								 | 
							
								                    *visible = pika_action_is_visible (cur_action);
							 | 
						||
| 
								 | 
							
								                  else
							 | 
						||
| 
								 | 
							
								                    /* This may happen when editing a menu item for an action
							 | 
						||
| 
								 | 
							
								                     * which got removed.
							 | 
						||
| 
								 | 
							
								                     */
							 | 
						||
| 
								 | 
							
								                    *visible = FALSE;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          else if (cur_action != NULL && pika_action_is_visible (cur_action))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              len++;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (action_name == NULL || iter != NULL, -1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return len;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gboolean
							 | 
						||
| 
								 | 
							
								pika_menu_model_is_mutable (GMenuModel* model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Public functions. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PikaMenuModel *
							 | 
						||
| 
								 | 
							
								pika_menu_model_new (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                     GMenuModel    *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return g_object_new (PIKA_TYPE_MENU_MODEL,
							 | 
						||
| 
								 | 
							
								                       "manager", manager,
							 | 
						||
| 
								 | 
							
								                       "model",   model,
							 | 
						||
| 
								 | 
							
								                       NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PikaMenuModel *
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_submodel (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                              const gchar   *path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *submodel;
							 | 
						||
| 
								 | 
							
								  gchar         *submenus;
							 | 
						||
| 
								 | 
							
								  gchar         *submenu;
							 | 
						||
| 
								 | 
							
								  gchar         *subsubmenus;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  submodel = g_object_ref (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (path == NULL)
							 | 
						||
| 
								 | 
							
								    return submodel;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  submenus    = g_strdup (path);
							 | 
						||
| 
								 | 
							
								  subsubmenus = submenus;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (subsubmenus && strlen (subsubmenus) > 0)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      gint n_items;
							 | 
						||
| 
								 | 
							
								      gint i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      submenu = subsubmenus;
							 | 
						||
| 
								 | 
							
								      while (*submenu == '/')
							 | 
						||
| 
								 | 
							
								        submenu++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsubmenus = strstr (submenu, "/");
							 | 
						||
| 
								 | 
							
								      if (subsubmenus)
							 | 
						||
| 
								 | 
							
								        *(subsubmenus++) = '\0';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (strlen (submenu) == 0)
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      n_items = g_menu_model_get_n_items (G_MENU_MODEL (submodel));
							 | 
						||
| 
								 | 
							
								      for (i = 0; i < n_items; i++)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          GMenuModel *subsubmodel;
							 | 
						||
| 
								 | 
							
								          gchar      *label = NULL;
							 | 
						||
| 
								 | 
							
								          gchar      *canon_label = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          subsubmodel = g_menu_model_get_item_link (G_MENU_MODEL (submodel), i, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								          g_menu_model_get_item_attribute (G_MENU_MODEL (submodel), i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
							 | 
						||
| 
								 | 
							
								          if (label)
							 | 
						||
| 
								 | 
							
								            canon_label = pika_utils_make_canonical_menu_label (label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (subsubmodel && g_strcmp0 (canon_label, submenu) == 0)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              g_object_unref (submodel);
							 | 
						||
| 
								 | 
							
								              submodel = PIKA_MENU_MODEL (subsubmodel);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_free (label);
							 | 
						||
| 
								 | 
							
								              g_free (canon_label);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          g_clear_object (&subsubmodel);
							 | 
						||
| 
								 | 
							
								          g_free (label);
							 | 
						||
| 
								 | 
							
								          g_free (canon_label);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      g_return_val_if_fail (i < n_items, NULL);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_free (submenus);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return submodel;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const gchar *
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_path (PikaMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  return model->priv->path;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_menu_model_set_title (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                           const gchar   *path,
							 | 
						||
| 
								 | 
							
								                           const gchar   *title)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *submenu = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = pika_menu_model_get_menu_item_rec (model, path, &submenu, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (item != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_menu_item_set_label (item, title);
							 | 
						||
| 
								 | 
							
								      g_object_notify (G_OBJECT (submenu), "title");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_menu_model_set_color (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                           const gchar   *path,
							 | 
						||
| 
								 | 
							
								                           const PikaRGB *color)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *submenu = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = pika_menu_model_get_menu_item_rec (model, path, &submenu, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (item != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (color == NULL)
							 | 
						||
| 
								 | 
							
								        g_clear_pointer (&submenu->priv->submenu_color, g_free);
							 | 
						||
| 
								 | 
							
								      else if (submenu->priv->submenu_color == NULL)
							 | 
						||
| 
								 | 
							
								        submenu->priv->submenu_color = g_new (PikaRGB, 1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (color != NULL)
							 | 
						||
| 
								 | 
							
								        *submenu->priv->submenu_color = *color;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_object_notify (G_OBJECT (submenu), "color");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Private functions. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static PikaMenuModel *
							 | 
						||
| 
								 | 
							
								pika_menu_model_new_section (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                             GMenuModel    *model,
							 | 
						||
| 
								 | 
							
								                             const gchar   *path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return g_object_new (PIKA_TYPE_MENU_MODEL,
							 | 
						||
| 
								 | 
							
								                       "manager", manager,
							 | 
						||
| 
								 | 
							
								                       "model",   model,
							 | 
						||
| 
								 | 
							
								                       "path",    path,
							 | 
						||
| 
								 | 
							
								                       "section", TRUE,
							 | 
						||
| 
								 | 
							
								                       NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static PikaMenuModel *
							 | 
						||
| 
								 | 
							
								pika_menu_model_new_submenu (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                             GMenuModel    *model,
							 | 
						||
| 
								 | 
							
								                             const gchar   *path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return g_object_new (PIKA_TYPE_MENU_MODEL,
							 | 
						||
| 
								 | 
							
								                       "manager", manager,
							 | 
						||
| 
								 | 
							
								                       "model",   model,
							 | 
						||
| 
								 | 
							
								                       "path",    path,
							 | 
						||
| 
								 | 
							
								                       NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_initialize (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                            GMenuModel    *gmodel)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gint n_items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_MENU_MODEL (model));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (gmodel == NULL || G_IS_MENU_MODEL (gmodel));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  n_items = gmodel != NULL ? g_menu_model_get_n_items (gmodel) : 0;
							 | 
						||
| 
								 | 
							
								  for (int i = 0; i < n_items; i++)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GMenuModel *subsection;
							 | 
						||
| 
								 | 
							
								      GMenuModel *submenu;
							 | 
						||
| 
								 | 
							
								      gchar      *label       = NULL;
							 | 
						||
| 
								 | 
							
								      gchar      *action_name = NULL;
							 | 
						||
| 
								 | 
							
								      GMenuItem  *item        = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsection = g_menu_model_get_item_link (G_MENU_MODEL (gmodel), i, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								      submenu    = g_menu_model_get_item_link (G_MENU_MODEL (gmodel), i, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								      g_menu_model_get_item_attribute (G_MENU_MODEL (gmodel), i,
							 | 
						||
| 
								 | 
							
								                                       G_MENU_ATTRIBUTE_LABEL, "s", &label);
							 | 
						||
| 
								 | 
							
								      g_menu_model_get_item_attribute (G_MENU_MODEL (gmodel), i,
							 | 
						||
| 
								 | 
							
								                                       G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (subsection != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaMenuModel *submodel;
							 | 
						||
| 
								 | 
							
								          gchar         *section_name = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          submodel = pika_menu_model_new_section (model->priv->manager, subsection,
							 | 
						||
| 
								 | 
							
								                                                  model->priv->path);
							 | 
						||
| 
								 | 
							
								          item = g_menu_item_new_section (label, G_MENU_MODEL (submodel));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (g_menu_model_get_item_attribute (G_MENU_MODEL (gmodel), i,
							 | 
						||
| 
								 | 
							
								                                               "section-name", "s", §ion_name))
							 | 
						||
| 
								 | 
							
								            g_hash_table_insert (model->priv->named_sections,
							 | 
						||
| 
								 | 
							
								                                 (gpointer) section_name,
							 | 
						||
| 
								 | 
							
								                                 item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_object_unref (submodel);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else if (submenu != NULL && label == NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaMenuModel *submodel;
							 | 
						||
| 
								 | 
							
								          PikaAction    *action;
							 | 
						||
| 
								 | 
							
								          gchar         *canon_label;
							 | 
						||
| 
								 | 
							
								          const gchar   *group_label;
							 | 
						||
| 
								 | 
							
								          gchar         *path;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_return_if_fail (action_name != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          action = pika_ui_manager_find_action (model->priv->manager, NULL, action_name);
							 | 
						||
| 
								 | 
							
								          /* As a special case, when a submenu has no label, we expect it to
							 | 
						||
| 
								 | 
							
								           * have an action attribute, which must be for a radio action. In such
							 | 
						||
| 
								 | 
							
								           * a case, we'll use the radio actions' group label as submenu title.
							 | 
						||
| 
								 | 
							
								           * See e.g.: menus/gradient-editor-menu.ui
							 | 
						||
| 
								 | 
							
								           */
							 | 
						||
| 
								 | 
							
								          g_return_if_fail (PIKA_IS_RADIO_ACTION (action));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          group_label = pika_radio_action_get_group_label (PIKA_RADIO_ACTION (action));
							 | 
						||
| 
								 | 
							
								          canon_label = pika_utils_make_canonical_menu_label (group_label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          path = g_strdup_printf ("%s/%s",
							 | 
						||
| 
								 | 
							
								                                  model->priv->path ? model->priv->path : "",
							 | 
						||
| 
								 | 
							
								                                  canon_label);
							 | 
						||
| 
								 | 
							
								          g_free (canon_label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          submodel = pika_menu_model_new_submenu (model->priv->manager, submenu, path);
							 | 
						||
| 
								 | 
							
								          item     = g_menu_item_new_submenu (group_label, G_MENU_MODEL (submodel));
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action, "notify::group-label",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_notify_group_label),
							 | 
						||
| 
								 | 
							
								                                   item, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_object_unref (submodel);
							 | 
						||
| 
								 | 
							
								          g_free (path);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else if (submenu != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaMenuModel *submodel;
							 | 
						||
| 
								 | 
							
								          gchar         *canon_label;
							 | 
						||
| 
								 | 
							
								          gchar         *path;
							 | 
						||
| 
								 | 
							
								          const gchar   *en_label;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_return_if_fail (label != NULL);
							 | 
						||
| 
								 | 
							
								          en_label = g_object_get_data (G_OBJECT (submenu),
							 | 
						||
| 
								 | 
							
								                                        "pika-ui-manager-menu-model-en-label");
							 | 
						||
| 
								 | 
							
								          g_return_if_fail (en_label != NULL);
							 | 
						||
| 
								 | 
							
								          canon_label = pika_utils_make_canonical_menu_label (en_label);
							 | 
						||
| 
								 | 
							
								          path = g_strdup_printf ("%s/%s",
							 | 
						||
| 
								 | 
							
								                                  model->priv->path ? model->priv->path : "",
							 | 
						||
| 
								 | 
							
								                                  canon_label);
							 | 
						||
| 
								 | 
							
								          g_free (canon_label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          submodel = pika_menu_model_new_submenu (model->priv->manager, submenu, path);
							 | 
						||
| 
								 | 
							
								          item     = g_menu_item_new_submenu (label, G_MENU_MODEL (submodel));
							 | 
						||
| 
								 | 
							
								          submodel->priv->submenu_item = item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_object_unref (submodel);
							 | 
						||
| 
								 | 
							
								          g_free (path);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaAction *action;
							 | 
						||
| 
								 | 
							
								          gchar      *label_variant = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          item = g_menu_item_new_from_model (G_MENU_MODEL (gmodel), i);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_return_if_fail (action_name != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          action = pika_ui_manager_find_action (model->priv->manager, NULL, action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (model->priv->manager->store_action_paths)
							 | 
						||
| 
								 | 
							
								            /* Special-case the main menu manager when constructing it as
							 | 
						||
| 
								 | 
							
								             * this is the only one which should set the menu path.
							 | 
						||
| 
								 | 
							
								             */
							 | 
						||
| 
								 | 
							
								            pika_action_set_menu_path (action, pika_menu_model_get_path (model));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::visible",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_visible),
							 | 
						||
| 
								 | 
							
								                                   model, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_menu_item_get_attribute (item, "label-variant", "s",
							 | 
						||
| 
								 | 
							
								                                     &label_variant);
							 | 
						||
| 
								 | 
							
								          if (g_strcmp0 (label_variant, "long") == 0)
							 | 
						||
| 
								 | 
							
								            g_menu_item_set_label (item, pika_action_get_label (action));
							 | 
						||
| 
								 | 
							
								          else
							 | 
						||
| 
								 | 
							
								            g_menu_item_set_label (item, pika_action_get_short_label (action));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::short-label",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_label),
							 | 
						||
| 
								 | 
							
								                                   item, 0);
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::label",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_label),
							 | 
						||
| 
								 | 
							
								                                   item, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          /* We want PikaRadioAction to be GTK_MENU_TRACKER_ITEM_ROLE_RADIO,
							 | 
						||
| 
								 | 
							
								           * in order to be displayed as radio menu items (as used to be
							 | 
						||
| 
								 | 
							
								           * GtkRadioAction) even in the gtk_application_set_menubar() code path.
							 | 
						||
| 
								 | 
							
								           *
							 | 
						||
| 
								 | 
							
								           * GTK do this by checking that the item has a "target" parameter,
							 | 
						||
| 
								 | 
							
								           * which we don't add in our .ui file. Instead let's do this
							 | 
						||
| 
								 | 
							
								           * programmatically, hence avoiding human errors.
							 | 
						||
| 
								 | 
							
								           * See: gtk/gtkmenutrackeritem.c
							 | 
						||
| 
								 | 
							
								           */
							 | 
						||
| 
								 | 
							
								          if (PIKA_IS_RADIO_ACTION (action))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              gint target;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_object_get (action, "value", &target, NULL);
							 | 
						||
| 
								 | 
							
								              g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_TARGET, "i", target);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_free (label_variant);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (item)
							 | 
						||
| 
								 | 
							
								        model->priv->items = g_list_append (model->priv->items, item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_free (label);
							 | 
						||
| 
								 | 
							
								      g_free (action_name);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (! model->priv->is_section)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_signal_connect_object (model->priv->manager, "ui-added",
							 | 
						||
| 
								 | 
							
								                               G_CALLBACK (pika_menu_model_ui_added),
							 | 
						||
| 
								 | 
							
								                               model, 0);
							 | 
						||
| 
								 | 
							
								      g_signal_connect_object (model->priv->manager, "ui-removed",
							 | 
						||
| 
								 | 
							
								                               G_CALLBACK (pika_menu_model_ui_removed),
							 | 
						||
| 
								 | 
							
								                               model, 0);
							 | 
						||
| 
								 | 
							
								      pika_ui_manager_foreach_ui (model->priv->manager,
							 | 
						||
| 
								 | 
							
								                                  (PikaUIMenuCallback) pika_menu_model_ui_added,
							 | 
						||
| 
								 | 
							
								                                  model);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Returns the directory to create as a new submodel to @model, with
							 | 
						||
| 
								 | 
							
								 * @canonical_path being the fully canonical path name (all slashes unique,
							 | 
						||
| 
								 | 
							
								 * leading slash, no trailing slash, section name removed and no mnemonic), e.g.
							 | 
						||
| 
								 | 
							
								 * "/some/path/name" and @mnemonic_path is the fully canonical path name, except
							 | 
						||
| 
								 | 
							
								 * that it contains mnemonics, e.g. "/_some/p_ath/_name".
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The code relies on these canonicalized characteristics so you must make sure
							 | 
						||
| 
								 | 
							
								 * to feed properly formatted paths.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The return value is the canonical path of the first submenu to create,
							 | 
						||
| 
								 | 
							
								 * including mnemonics, e.g. if @model was for path "/some", then this function
							 | 
						||
| 
								 | 
							
								 * returns "p_ath".
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								static gchar *
							 | 
						||
| 
								 | 
							
								pika_menu_model_handles_subpath (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                                 const gchar   *canonical_path,
							 | 
						||
| 
								 | 
							
								                                 const gchar   *mnemonic_path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar *new_dir;
							 | 
						||
| 
								 | 
							
								  gchar *end_new_dir;
							 | 
						||
| 
								 | 
							
								  gint   n_slash = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (model->priv->path != NULL &&
							 | 
						||
| 
								 | 
							
								      (! g_str_has_prefix (canonical_path, model->priv->path) ||
							 | 
						||
| 
								 | 
							
								       canonical_path[strlen (model->priv->path)] != '/'))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      return FALSE;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (GList *iter = model->priv->items; iter; iter = iter->next)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GMenuModel *submenu    = NULL;
							 | 
						||
| 
								 | 
							
								      GMenuModel *subsection = NULL;
							 | 
						||
| 
								 | 
							
								      GMenuItem  *item       = iter->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsection = g_menu_item_get_link (item, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (subsection != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          /* Checking if a subsection is a sub-path only gives a partial view of
							 | 
						||
| 
								 | 
							
								           * the whole menu. So we only handle the negative result (which means
							 | 
						||
| 
								 | 
							
								           * we found a submenu model which will handle this path instead).
							 | 
						||
| 
								 | 
							
								           */
							 | 
						||
| 
								 | 
							
								          new_dir = pika_menu_model_handles_subpath (PIKA_MENU_MODEL (subsection),
							 | 
						||
| 
								 | 
							
								                                                     canonical_path, mnemonic_path);
							 | 
						||
| 
								 | 
							
								          if (new_dir == NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								              return NULL;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          else
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              g_free (new_dir);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else if ((submenu = g_menu_item_get_link (item, G_MENU_LINK_SUBMENU)) != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gchar *subpath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          subpath = g_strdup_printf ("%s/", PIKA_MENU_MODEL (submenu)->priv->path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (g_strcmp0 (canonical_path, PIKA_MENU_MODEL (submenu)->priv->path) == 0 ||
							 | 
						||
| 
								 | 
							
								              g_str_has_prefix (canonical_path, subpath))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              /* A submodel will handle the new path. */
							 | 
						||
| 
								 | 
							
								              g_free (subpath);
							 | 
						||
| 
								 | 
							
								              g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								              g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              return NULL;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_free (subpath);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (model->priv->path != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      n_slash = 1;
							 | 
						||
| 
								 | 
							
								      new_dir = model->priv->path;
							 | 
						||
| 
								 | 
							
								      while ((new_dir = strstr (new_dir + 1, "/")) != NULL)
							 | 
						||
| 
								 | 
							
								        n_slash++;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  new_dir = (gchar *) mnemonic_path + 1;
							 | 
						||
| 
								 | 
							
								  while (n_slash-- > 0)
							 | 
						||
| 
								 | 
							
								    new_dir = strstr (new_dir, "/") + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  end_new_dir = strstr (new_dir, "/");
							 | 
						||
| 
								 | 
							
								  if (end_new_dir)
							 | 
						||
| 
								 | 
							
								    new_dir = g_strndup (new_dir, end_new_dir - new_dir);
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    new_dir = g_strdup (new_dir);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return new_dir;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GMenuItem *
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_item (PikaMenuModel *model,
							 | 
						||
| 
								 | 
							
								                          gint           idx)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *m   = PIKA_MENU_MODEL (model);
							 | 
						||
| 
								 | 
							
								  gint           cur = -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (GList *iter = m->priv->items; iter; iter = iter->next)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GMenuModel  *subsection;
							 | 
						||
| 
								 | 
							
								      GMenuModel  *submenu;
							 | 
						||
| 
								 | 
							
								      const gchar *action_name = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								      submenu    = g_menu_item_get_link (iter->data, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (subsection || submenu)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          cur++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      /* Do not count invisible actions. */
							 | 
						||
| 
								 | 
							
								      else if (g_menu_item_get_attribute (iter->data,
							 | 
						||
| 
								 | 
							
								                                          G_MENU_ATTRIBUTE_ACTION,
							 | 
						||
| 
								 | 
							
								                                          "&s", &action_name))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          PikaAction *action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          action = pika_ui_manager_find_action (model->priv->manager, NULL,
							 | 
						||
| 
								 | 
							
								                                                action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (pika_action_is_visible (action))
							 | 
						||
| 
								 | 
							
								            cur++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (cur == idx)
							 | 
						||
| 
								 | 
							
								        return iter->data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return NULL;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GMenuItem *
							 | 
						||
| 
								 | 
							
								pika_menu_model_get_menu_item_rec (PikaMenuModel  *model,
							 | 
						||
| 
								 | 
							
								                                   const gchar    *path,
							 | 
						||
| 
								 | 
							
								                                   PikaMenuModel **menu,
							 | 
						||
| 
								 | 
							
								                                   GMenuItem      *item)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (item == model->priv->submenu_item, NULL);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (menu != NULL && *menu == NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (pika_utils_are_menu_path_identical (path, model->priv->path, NULL, NULL, NULL))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      *menu = model;
							 | 
						||
| 
								 | 
							
								      return item;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GList     *iter;
							 | 
						||
| 
								 | 
							
								      GMenuItem *found = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (iter = model->priv->items; iter; iter = iter->next)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          GMenuItem  *subitem = iter->data;
							 | 
						||
| 
								 | 
							
								          GMenuModel *submenu = NULL;
							 | 
						||
| 
								 | 
							
								          GMenuModel *section = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          submenu = g_menu_item_get_link (subitem, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								          section = g_menu_item_get_link (subitem, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (section != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              found = pika_menu_model_get_menu_item_rec (PIKA_MENU_MODEL (section), path, menu, NULL);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          else if (submenu != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              gchar *subpath;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              subpath = g_strdup_printf ("%s/", PIKA_MENU_MODEL (submenu)->priv->path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              if (g_strcmp0 (path, PIKA_MENU_MODEL (submenu)->priv->path) == 0 ||
							 | 
						||
| 
								 | 
							
								                  g_str_has_prefix (path, subpath))
							 | 
						||
| 
								 | 
							
								                {
							 | 
						||
| 
								 | 
							
								                  found = pika_menu_model_get_menu_item_rec (PIKA_MENU_MODEL (submenu), path, menu, subitem);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_free (subpath);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								          g_clear_object (§ion);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (found != NULL)
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return found;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_notify_group_label (PikaRadioAction  *action,
							 | 
						||
| 
								 | 
							
								                                    const GParamSpec *pspec,
							 | 
						||
| 
								 | 
							
								                                    GMenuItem        *item)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_menu_item_set_label (item, pika_radio_action_get_group_label (action));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_action_notify_visible (PikaAction    *action,
							 | 
						||
| 
								 | 
							
								                                       GParamSpec    *pspec,
							 | 
						||
| 
								 | 
							
								                                       PikaMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gint     pos;
							 | 
						||
| 
								 | 
							
								  gboolean visible;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  pos = pika_menu_model_get_position (model, pika_action_get_name (action),
							 | 
						||
| 
								 | 
							
								                                      &visible);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (visible)
							 | 
						||
| 
								 | 
							
								    g_menu_model_items_changed (G_MENU_MODEL (model), pos, 0, 1);
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    g_menu_model_items_changed (G_MENU_MODEL (model), pos, 1, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_menu_model_action_notify_label (PikaAction *action,
							 | 
						||
| 
								 | 
							
								                                     GParamSpec *pspec,
							 | 
						||
| 
								 | 
							
								                                     GMenuItem  *item)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar *label_variant = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_ACTION (action));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (G_IS_MENU_ITEM (item));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_menu_item_get_attribute (item, "label-variant", "s", &label_variant);
							 | 
						||
| 
								 | 
							
								  if (g_strcmp0 (label_variant, "long") == 0)
							 | 
						||
| 
								 | 
							
								    g_menu_item_set_label (item, pika_action_get_label (action));
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    g_menu_item_set_label (item, pika_action_get_short_label (action));
							 | 
						||
| 
								 | 
							
								  g_free (label_variant);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gboolean
							 | 
						||
| 
								 | 
							
								pika_menu_model_ui_added (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                          const gchar   *path,
							 | 
						||
| 
								 | 
							
								                          const gchar   *action_name,
							 | 
						||
| 
								 | 
							
								                          gboolean       top,
							 | 
						||
| 
								 | 
							
								                          PikaMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar         *canonical_path = NULL;
							 | 
						||
| 
								 | 
							
								  gchar         *mnemonic_path  = NULL;
							 | 
						||
| 
								 | 
							
								  gchar         *section_name   = NULL;
							 | 
						||
| 
								 | 
							
								  gboolean       added          = FALSE;
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *mod_model      = g_object_ref (model);
							 | 
						||
| 
								 | 
							
								  gchar         *new_dir;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (pika_utils_are_menu_path_identical (path, model->priv->path, &canonical_path,
							 | 
						||
| 
								 | 
							
								                                          &mnemonic_path, §ion_name))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GApplication *app = model->priv->manager->pika->app;
							 | 
						||
| 
								 | 
							
								      GAction      *action;
							 | 
						||
| 
								 | 
							
								      gchar        *detailed_action_name;
							 | 
						||
| 
								 | 
							
								      const gchar  *action_prefix = "app";
							 | 
						||
| 
								 | 
							
								      GMenuItem    *item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
							 | 
						||
| 
								 | 
							
								      if (action == NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          action = (GAction *) pika_ui_manager_find_action (manager, NULL, action_name);
							 | 
						||
| 
								 | 
							
								          if (action != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              PikaActionGroup *group;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              group = pika_action_get_group (PIKA_ACTION (action));
							 | 
						||
| 
								 | 
							
								              if (group != NULL)
							 | 
						||
| 
								 | 
							
								                action_prefix = pika_action_group_get_name (group);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_return_val_if_fail (action != NULL, FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      added = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (section_name != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          GMenuItem *section_item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          section_item = g_hash_table_lookup (model->priv->named_sections, section_name);
							 | 
						||
| 
								 | 
							
								          if (section_item)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              g_object_unref (mod_model);
							 | 
						||
| 
								 | 
							
								              mod_model = PIKA_MENU_MODEL (g_menu_item_get_link (section_item, G_MENU_LINK_SECTION));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_signal_handlers_disconnect_by_func (action,
							 | 
						||
| 
								 | 
							
								                                            G_CALLBACK (pika_menu_model_action_notify_visible),
							 | 
						||
| 
								 | 
							
								                                            mod_model);
							 | 
						||
| 
								 | 
							
								      detailed_action_name = g_strdup_printf ("%s.%s", action_prefix, g_action_get_name (action));
							 | 
						||
| 
								 | 
							
								      item = g_menu_item_new (pika_action_get_short_label (PIKA_ACTION (action)), detailed_action_name);
							 | 
						||
| 
								 | 
							
								      /* TODO: add also G_MENU_ATTRIBUTE_ICON attribute? */
							 | 
						||
| 
								 | 
							
								      g_free (detailed_action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (model->priv->manager->store_action_paths)
							 | 
						||
| 
								 | 
							
								        pika_action_set_menu_path (PIKA_ACTION (action), pika_menu_model_get_path (model));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (top)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          mod_model->priv->items = g_list_prepend (mod_model->priv->items, item);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          mod_model->priv->items = g_list_append (mod_model->priv->items, item);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (added)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gint position = pika_menu_model_get_position (mod_model, action_name, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::visible",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_visible),
							 | 
						||
| 
								 | 
							
								                                   mod_model, 0);
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::short-label",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_label),
							 | 
						||
| 
								 | 
							
								                                   item, 0);
							 | 
						||
| 
								 | 
							
								          g_signal_connect_object (action,
							 | 
						||
| 
								 | 
							
								                                   "notify::label",
							 | 
						||
| 
								 | 
							
								                                   G_CALLBACK (pika_menu_model_action_notify_label),
							 | 
						||
| 
								 | 
							
								                                   item, 0);
							 | 
						||
| 
								 | 
							
								          g_menu_model_items_changed (G_MENU_MODEL (mod_model), position, 0, 1);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          g_object_unref (item);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  else if ((new_dir = pika_menu_model_handles_subpath (model, canonical_path, mnemonic_path)))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaMenuModel *submodel;
							 | 
						||
| 
								 | 
							
								      GMenuItem     *item;
							 | 
						||
| 
								 | 
							
								      gchar         *canon_label;
							 | 
						||
| 
								 | 
							
								      gchar         *submodel_path;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      canon_label   = pika_utils_make_canonical_menu_label (new_dir);
							 | 
						||
| 
								 | 
							
								      submodel_path = g_strdup_printf ("%s/%s",
							 | 
						||
| 
								 | 
							
								                                       model->priv->path ? model->priv->path : "",
							 | 
						||
| 
								 | 
							
								                                       canon_label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      submodel = pika_menu_model_new_submenu (model->priv->manager, NULL, submodel_path);
							 | 
						||
| 
								 | 
							
								      item     = g_menu_item_new_submenu (new_dir, G_MENU_MODEL (submodel));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (model->priv->path == NULL)
							 | 
						||
| 
								 | 
							
								        model->priv->items = g_list_insert (model->priv->items, item,
							 | 
						||
| 
								 | 
							
								                                            g_list_length (model->priv->items) - 2);
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        model->priv->items = g_list_append (model->priv->items, item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_free (canon_label);
							 | 
						||
| 
								 | 
							
								      g_object_unref (submodel);
							 | 
						||
| 
								 | 
							
								      g_free (submodel_path);
							 | 
						||
| 
								 | 
							
								      g_free (new_dir);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_menu_model_items_changed (G_MENU_MODEL (model),
							 | 
						||
| 
								 | 
							
								                                  pika_menu_model_get_position (model, NULL, NULL),
							 | 
						||
| 
								 | 
							
								                                  1, 0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_clear_object (&mod_model);
							 | 
						||
| 
								 | 
							
								  g_free (canonical_path);
							 | 
						||
| 
								 | 
							
								  g_free (mnemonic_path);
							 | 
						||
| 
								 | 
							
								  g_free (section_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return added;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gboolean
							 | 
						||
| 
								 | 
							
								pika_menu_model_ui_removed (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                            const gchar   *path,
							 | 
						||
| 
								 | 
							
								                            const gchar   *action_name,
							 | 
						||
| 
								 | 
							
								                            PikaMenuModel *model)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar    *section_name = NULL;
							 | 
						||
| 
								 | 
							
								  gboolean  removed      = FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (pika_utils_are_menu_path_identical (path, model->priv->path, NULL, NULL, §ion_name))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GApplication *app         = model->priv->manager->pika->app;
							 | 
						||
| 
								 | 
							
								      GMenuItem    *item        = NULL;
							 | 
						||
| 
								 | 
							
								      GMenuModel   *subsection  = NULL;
							 | 
						||
| 
								 | 
							
								      GAction      *action;
							 | 
						||
| 
								 | 
							
								      GList        *iter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      removed = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (iter = model->priv->items; iter; iter = iter->next)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          const gchar *action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (subsection != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              if (pika_menu_model_ui_removed (manager, path, action_name,
							 | 
						||
| 
								 | 
							
								                                              PIKA_MENU_MODEL (subsection)))
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								              else
							 | 
						||
| 
								 | 
							
								                g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          else if (g_menu_item_get_attribute (iter->data,
							 | 
						||
| 
								 | 
							
								                                              G_MENU_ATTRIBUTE_ACTION,
							 | 
						||
| 
								 | 
							
								                                              "&s", &action) &&
							 | 
						||
| 
								 | 
							
								                   /* "action" attribute will start with "app." prefix. */
							 | 
						||
| 
								 | 
							
								                   g_strcmp0 (action + 4, action_name) == 0)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              item = iter->data;
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (item)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gint position;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          position = pika_menu_model_get_position (model, action_name, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (action != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              g_signal_handlers_disconnect_by_func (action,
							 | 
						||
| 
								 | 
							
								                                                    G_CALLBACK (pika_menu_model_action_notify_visible),
							 | 
						||
| 
								 | 
							
								                                                    model);
							 | 
						||
| 
								 | 
							
								              g_signal_handlers_disconnect_by_func (action,
							 | 
						||
| 
								 | 
							
								                                                    G_CALLBACK (pika_menu_model_action_notify_label),
							 | 
						||
| 
								 | 
							
								                                                    item);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          g_object_unref (item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          model->priv->items = g_list_delete_link (model->priv->items, iter);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 0);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          removed = FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (subsection == NULL && ! model->priv->is_section)
							 | 
						||
| 
								 | 
							
								            g_warning ("%s: no item for action name '%s'.", G_STRFUNC, action_name);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      /* else: removed in a subsection. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  g_free (section_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return removed;
							 | 
						||
| 
								 | 
							
								}
							 |