1250 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1250 lines
		
	
	
		
			39 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
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * pikauimanager.c
							 | 
						||
| 
								 | 
							
								 * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 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 <string.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <gegl.h>
							 | 
						||
| 
								 | 
							
								#include <gtk/gtk.h>
							 | 
						||
| 
								 | 
							
								#include <gdk/gdkkeysyms.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "libpikabase/pikabase.h"
							 | 
						||
| 
								 | 
							
								#include "libpikawidgets/pikawidgets.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "widgets-types.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "config/pikaxmlparser.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "core/pika.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "pikaaction.h"
							 | 
						||
| 
								 | 
							
								#include "pikaactiongroup.h"
							 | 
						||
| 
								 | 
							
								#include "pikaradioaction.h"
							 | 
						||
| 
								 | 
							
								#include "pikahelp.h"
							 | 
						||
| 
								 | 
							
								#include "pikahelp-ids.h"
							 | 
						||
| 
								 | 
							
								#include "pikamenu.h"
							 | 
						||
| 
								 | 
							
								#include "pikamenumodel.h"
							 | 
						||
| 
								 | 
							
								#include "pikamenushell.h"
							 | 
						||
| 
								 | 
							
								#include "pikatoggleaction.h"
							 | 
						||
| 
								 | 
							
								#include "pikauimanager.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "pika-intl.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PROP_0,
							 | 
						||
| 
								 | 
							
								  PROP_NAME,
							 | 
						||
| 
								 | 
							
								  PROP_PIKA
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								enum
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  UPDATE,
							 | 
						||
| 
								 | 
							
								  SHOW_TOOLTIP,
							 | 
						||
| 
								 | 
							
								  HIDE_TOOLTIP,
							 | 
						||
| 
								 | 
							
								  UI_ADDED,
							 | 
						||
| 
								 | 
							
								  UI_REMOVED,
							 | 
						||
| 
								 | 
							
								  LAST_SIGNAL
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar    *path;
							 | 
						||
| 
								 | 
							
								  gchar    *action_name;
							 | 
						||
| 
								 | 
							
								  gboolean  top;
							 | 
						||
| 
								 | 
							
								} PikaUIManagerMenuItem;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_constructed          (GObject        *object);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_dispose              (GObject        *object);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_finalize             (GObject        *object);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_set_property         (GObject        *object,
							 | 
						||
| 
								 | 
							
								                                                        guint           prop_id,
							 | 
						||
| 
								 | 
							
								                                                        const GValue   *value,
							 | 
						||
| 
								 | 
							
								                                                        GParamSpec     *pspec);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_get_property         (GObject        *object,
							 | 
						||
| 
								 | 
							
								                                                        guint           prop_id,
							 | 
						||
| 
								 | 
							
								                                                        GValue         *value,
							 | 
						||
| 
								 | 
							
								                                                        GParamSpec     *pspec);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_real_update          (PikaUIManager  *manager,
							 | 
						||
| 
								 | 
							
								                                                        gpointer        update_data);
							 | 
						||
| 
								 | 
							
								static PikaUIManagerUIEntry *
							 | 
						||
| 
								 | 
							
								                  pika_ui_manager_entry_get            (PikaUIManager  *manager,
							 | 
						||
| 
								 | 
							
								                                                        const gchar    *ui_path);
							 | 
						||
| 
								 | 
							
								static PikaUIManagerUIEntry *
							 | 
						||
| 
								 | 
							
								                  pika_ui_manager_entry_ensure         (PikaUIManager  *manager,
							 | 
						||
| 
								 | 
							
								                                                        const gchar    *path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_store_builder_path   (GMenuModel           *model,
							 | 
						||
| 
								 | 
							
								                                                        GMenuModel           *original);
							 | 
						||
| 
								 | 
							
								static GtkBuilder * pika_ui_manager_load_builder       (const gchar          *filename,
							 | 
						||
| 
								 | 
							
								                                                        const gchar          *root);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_parse_start_element  (GMarkupParseContext  *context,
							 | 
						||
| 
								 | 
							
								                                                        const gchar          *element_name,
							 | 
						||
| 
								 | 
							
								                                                        const gchar         **attribute_names,
							 | 
						||
| 
								 | 
							
								                                                        const gchar         **attribute_values,
							 | 
						||
| 
								 | 
							
								                                                        gpointer              user_data,
							 | 
						||
| 
								 | 
							
								                                                        GError              **error);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_parse_end_element    (GMarkupParseContext  *context,
							 | 
						||
| 
								 | 
							
								                                                        const gchar          *element_name,
							 | 
						||
| 
								 | 
							
								                                                        gpointer              user_data,
							 | 
						||
| 
								 | 
							
								                                                        GError              **error);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_parse_ui_text        (GMarkupParseContext  *context,
							 | 
						||
| 
								 | 
							
								                                                        const gchar          *text,
							 | 
						||
| 
								 | 
							
								                                                        gsize                 text_len,
							 | 
						||
| 
								 | 
							
								                                                        gpointer              user_data,
							 | 
						||
| 
								 | 
							
								                                                        GError              **error);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_delete_popdown_data  (GtkWidget      *widget,
							 | 
						||
| 
								 | 
							
								                                                        PikaUIManager  *manager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_menu_item_free       (PikaUIManagerMenuItem *item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_popup_hidden         (GtkMenuShell          *popup,
							 | 
						||
| 
								 | 
							
								                                                        gpointer               user_data);
							 | 
						||
| 
								 | 
							
								static gboolean   pika_ui_manager_popup_destroy        (GtkWidget             *popup);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_image_action_added   (PikaActionGroup       *group,
							 | 
						||
| 
								 | 
							
								                                                        gchar                 *action_name,
							 | 
						||
| 
								 | 
							
								                                                        PikaUIManager         *manager);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_image_action_removed (PikaActionGroup       *group,
							 | 
						||
| 
								 | 
							
								                                                        gchar                 *action_name,
							 | 
						||
| 
								 | 
							
								                                                        PikaUIManager         *manager);
							 | 
						||
| 
								 | 
							
								static void       pika_ui_manager_image_accels_changed (PikaAction            *action,
							 | 
						||
| 
								 | 
							
								                                                        const gchar          **accels,
							 | 
						||
| 
								 | 
							
								                                                        PikaUIManager         *manager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								G_DEFINE_TYPE (PikaUIManager, pika_ui_manager, PIKA_TYPE_OBJECT)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define parent_class pika_ui_manager_parent_class
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static guint manager_signals[LAST_SIGNAL] = { 0 };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_class_init (PikaUIManagerClass *klass)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GObjectClass *object_class = G_OBJECT_CLASS (klass);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  object_class->constructed    = pika_ui_manager_constructed;
							 | 
						||
| 
								 | 
							
								  object_class->dispose        = pika_ui_manager_dispose;
							 | 
						||
| 
								 | 
							
								  object_class->finalize       = pika_ui_manager_finalize;
							 | 
						||
| 
								 | 
							
								  object_class->set_property   = pika_ui_manager_set_property;
							 | 
						||
| 
								 | 
							
								  object_class->get_property   = pika_ui_manager_get_property;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  klass->update                = pika_ui_manager_real_update;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager_signals[UPDATE] =
							 | 
						||
| 
								 | 
							
								    g_signal_new ("update",
							 | 
						||
| 
								 | 
							
								                  G_TYPE_FROM_CLASS (klass),
							 | 
						||
| 
								 | 
							
								                  G_SIGNAL_RUN_LAST,
							 | 
						||
| 
								 | 
							
								                  G_STRUCT_OFFSET (PikaUIManagerClass, update),
							 | 
						||
| 
								 | 
							
								                  NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE, 1,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_POINTER);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager_signals[SHOW_TOOLTIP] =
							 | 
						||
| 
								 | 
							
								    g_signal_new ("show-tooltip",
							 | 
						||
| 
								 | 
							
								                  G_TYPE_FROM_CLASS (klass),
							 | 
						||
| 
								 | 
							
								                  G_SIGNAL_RUN_LAST,
							 | 
						||
| 
								 | 
							
								                  G_STRUCT_OFFSET (PikaUIManagerClass, show_tooltip),
							 | 
						||
| 
								 | 
							
								                  NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE, 1,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_STRING);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager_signals[HIDE_TOOLTIP] =
							 | 
						||
| 
								 | 
							
								    g_signal_new ("hide-tooltip",
							 | 
						||
| 
								 | 
							
								                  G_TYPE_FROM_CLASS (klass),
							 | 
						||
| 
								 | 
							
								                  G_SIGNAL_RUN_LAST,
							 | 
						||
| 
								 | 
							
								                  G_STRUCT_OFFSET (PikaUIManagerClass, hide_tooltip),
							 | 
						||
| 
								 | 
							
								                  NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE, 0,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE);
							 | 
						||
| 
								 | 
							
								  manager_signals[UI_ADDED] =
							 | 
						||
| 
								 | 
							
								    g_signal_new ("ui-added",
							 | 
						||
| 
								 | 
							
								                  G_TYPE_FROM_CLASS (klass),
							 | 
						||
| 
								 | 
							
								                  G_SIGNAL_RUN_LAST,
							 | 
						||
| 
								 | 
							
								                  G_STRUCT_OFFSET (PikaUIManagerClass, ui_added),
							 | 
						||
| 
								 | 
							
								                  NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE, 3,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_STRING,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_STRING,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_BOOLEAN);
							 | 
						||
| 
								 | 
							
								  manager_signals[UI_REMOVED] =
							 | 
						||
| 
								 | 
							
								    g_signal_new ("ui-removed",
							 | 
						||
| 
								 | 
							
								                  G_TYPE_FROM_CLASS (klass),
							 | 
						||
| 
								 | 
							
								                  G_SIGNAL_RUN_LAST,
							 | 
						||
| 
								 | 
							
								                  G_STRUCT_OFFSET (PikaUIManagerClass, ui_removed),
							 | 
						||
| 
								 | 
							
								                  NULL, NULL, NULL,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_NONE, 2,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_STRING,
							 | 
						||
| 
								 | 
							
								                  G_TYPE_STRING);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_NAME,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_string ("name",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        NULL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_CONSTRUCT_ONLY));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_object_class_install_property (object_class, PROP_PIKA,
							 | 
						||
| 
								 | 
							
								                                   g_param_spec_object ("pika",
							 | 
						||
| 
								 | 
							
								                                                        NULL, NULL,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_TYPE_PIKA,
							 | 
						||
| 
								 | 
							
								                                                        PIKA_PARAM_READWRITE |
							 | 
						||
| 
								 | 
							
								                                                        G_PARAM_CONSTRUCT_ONLY));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  klass->managers = g_hash_table_new_full (g_str_hash, g_str_equal,
							 | 
						||
| 
								 | 
							
								                                           g_free, NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_init (PikaUIManager *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  manager->name               = NULL;
							 | 
						||
| 
								 | 
							
								  manager->pika               = NULL;
							 | 
						||
| 
								 | 
							
								  manager->action_groups      = NULL;
							 | 
						||
| 
								 | 
							
								  manager->store_action_paths = FALSE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_constructed (GObject *object)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager = PIKA_UI_MANAGER (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  G_OBJECT_CLASS (parent_class)->constructed (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (manager->name)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerClass *manager_class;
							 | 
						||
| 
								 | 
							
								      GList              *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      manager_class = PIKA_UI_MANAGER_GET_CLASS (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      list = g_hash_table_lookup (manager_class->managers, manager->name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      list = g_list_append (list, manager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_hash_table_replace (manager_class->managers,
							 | 
						||
| 
								 | 
							
								                            g_strdup (manager->name), list);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_dispose (GObject *object)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager = PIKA_UI_MANAGER (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (manager->name)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerClass *manager_class;
							 | 
						||
| 
								 | 
							
								      GList              *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      manager_class = PIKA_UI_MANAGER_GET_CLASS (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      list = g_hash_table_lookup (manager_class->managers, manager->name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (list)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          list = g_list_remove (list, manager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (list)
							 | 
						||
| 
								 | 
							
								            g_hash_table_replace (manager_class->managers,
							 | 
						||
| 
								 | 
							
								                                  g_strdup (manager->name), list);
							 | 
						||
| 
								 | 
							
								          else
							 | 
						||
| 
								 | 
							
								            g_hash_table_remove (manager_class->managers, manager->name);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  G_OBJECT_CLASS (parent_class)->dispose (object);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_finalize (GObject *object)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager = PIKA_UI_MANAGER (object);
							 | 
						||
| 
								 | 
							
								  GList         *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (list = manager->registered_uis; list; list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerUIEntry *entry = list->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_free (entry->ui_path);
							 | 
						||
| 
								 | 
							
								      g_free (entry->basename);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&entry->builder);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_slice_free (PikaUIManagerUIEntry, entry);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_clear_pointer (&manager->registered_uis, g_list_free);
							 | 
						||
| 
								 | 
							
								  g_clear_pointer (&manager->name,           g_free);
							 | 
						||
| 
								 | 
							
								  g_list_free_full (manager->action_groups,  g_object_unref);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_list_free_full (manager->ui_items,
							 | 
						||
| 
								 | 
							
								                    (GDestroyNotify) pika_ui_manager_menu_item_free);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  G_OBJECT_CLASS (parent_class)->finalize (object);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_set_property (GObject      *object,
							 | 
						||
| 
								 | 
							
								                              guint         prop_id,
							 | 
						||
| 
								 | 
							
								                              const GValue *value,
							 | 
						||
| 
								 | 
							
								                              GParamSpec   *pspec)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager = PIKA_UI_MANAGER (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch (prop_id)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								    case PROP_NAME:
							 | 
						||
| 
								 | 
							
								      g_free (manager->name);
							 | 
						||
| 
								 | 
							
								      manager->name = g_value_dup_string (value);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case PROP_PIKA:
							 | 
						||
| 
								 | 
							
								      manager->pika = g_value_get_object (value);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_get_property (GObject    *object,
							 | 
						||
| 
								 | 
							
								                              guint       prop_id,
							 | 
						||
| 
								 | 
							
								                              GValue     *value,
							 | 
						||
| 
								 | 
							
								                              GParamSpec *pspec)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager = PIKA_UI_MANAGER (object);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch (prop_id)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								    case PROP_NAME:
							 | 
						||
| 
								 | 
							
								      g_value_set_string (value, manager->name);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case PROP_PIKA:
							 | 
						||
| 
								 | 
							
								      g_value_set_object (value, manager->pika);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_real_update (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                             gpointer       update_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (list = pika_ui_manager_get_action_groups (manager);
							 | 
						||
| 
								 | 
							
								       list;
							 | 
						||
| 
								 | 
							
								       list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      pika_action_group_update (list->data, update_data);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * pika_ui_manager_new:
							 | 
						||
| 
								 | 
							
								 * @pika: the @Pika instance this ui manager belongs to
							 | 
						||
| 
								 | 
							
								 * @name: the UI manager's name.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Creates a new #PikaUIManager object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Returns: the new #PikaUIManager
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								PikaUIManager *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_new (Pika        *pika,
							 | 
						||
| 
								 | 
							
								                     const gchar *name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManager *manager;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager = g_object_new (PIKA_TYPE_UI_MANAGER,
							 | 
						||
| 
								 | 
							
								                          "name", name,
							 | 
						||
| 
								 | 
							
								                          "pika", pika,
							 | 
						||
| 
								 | 
							
								                          NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return manager;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								GList *
							 | 
						||
| 
								 | 
							
								pika_ui_managers_from_name (const gchar *name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManagerClass *manager_class;
							 | 
						||
| 
								 | 
							
								  GList              *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (name != NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager_class = g_type_class_ref (PIKA_TYPE_UI_MANAGER);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  list = g_hash_table_lookup (manager_class->managers, name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_type_class_unref (manager_class);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return list;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_update (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                        gpointer       update_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_signal_emit (manager, manager_signals[UPDATE], 0, update_data);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_add_action_group (PikaUIManager   *manager,
							 | 
						||
| 
								 | 
							
								                                  PikaActionGroup *group)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  if (! g_list_find (manager->action_groups, group))
							 | 
						||
| 
								 | 
							
								    manager->action_groups = g_list_prepend (manager->action_groups, g_object_ref (group));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /* Special-case the <Image> UI Manager which should be unique and represent
							 | 
						||
| 
								 | 
							
								   * global application actions.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  if (g_strcmp0 (manager->name, "<Image>") == 0)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      gchar **actions = g_action_group_list_actions (G_ACTION_GROUP (group));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (int i = 0; i < g_strv_length (actions); i++)
							 | 
						||
| 
								 | 
							
								        pika_ui_manager_image_action_added (group, actions[i], manager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_signal_connect_object (group, "action-added",
							 | 
						||
| 
								 | 
							
								                               G_CALLBACK (pika_ui_manager_image_action_added),
							 | 
						||
| 
								 | 
							
								                               manager, 0);
							 | 
						||
| 
								 | 
							
								      g_signal_connect_object (group, "action-removed",
							 | 
						||
| 
								 | 
							
								                               G_CALLBACK (pika_ui_manager_image_action_removed),
							 | 
						||
| 
								 | 
							
								                               manager, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_strfreev (actions);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PikaActionGroup *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_get_action_group (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                                  const gchar   *name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), NULL);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (name != NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (list = pika_ui_manager_get_action_groups (manager);
							 | 
						||
| 
								 | 
							
								       list;
							 | 
						||
| 
								 | 
							
								       list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaActionGroup *group = list->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (! strcmp (name, pika_action_group_get_name (group)))
							 | 
						||
| 
								 | 
							
								        return group;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return NULL;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								GList *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_get_action_groups (PikaUIManager *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  return manager->action_groups;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PikaMenuModel *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_get_model (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                           const gchar   *path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManagerUIEntry *entry;
							 | 
						||
| 
								 | 
							
								  PikaMenuModel        *model;
							 | 
						||
| 
								 | 
							
								  GMenuModel           *gmodel;
							 | 
						||
| 
								 | 
							
								  PikaMenuModel        *submodel;
							 | 
						||
| 
								 | 
							
								  gchar                *root;
							 | 
						||
| 
								 | 
							
								  gchar                *submenus;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  root     = g_strdup (path);
							 | 
						||
| 
								 | 
							
								  submenus = strstr (root + 1, "/");
							 | 
						||
| 
								 | 
							
								  if (submenus != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      *submenus = '\0';
							 | 
						||
| 
								 | 
							
								      if (*(++submenus) == '\0')
							 | 
						||
| 
								 | 
							
								        submenus = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  entry         = pika_ui_manager_entry_ensure (manager, path);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (entry != NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (entry->builder == NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      const gchar *menus_path_override = g_getenv ("PIKA_TESTING_MENUS_PATH");
							 | 
						||
| 
								 | 
							
								      gchar       *full_basename;
							 | 
						||
| 
								 | 
							
								      gchar       *filename;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      full_basename = g_strconcat (entry->basename, ".ui", NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /* In order for test cases to be able to run without PIKA being
							 | 
						||
| 
								 | 
							
								       * installed yet, allow them to override the menus directory to the
							 | 
						||
| 
								 | 
							
								       * menus dir in the source root
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      if (menus_path_override)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          GList *paths = pika_path_parse (menus_path_override, 2, FALSE, NULL);
							 | 
						||
| 
								 | 
							
								          GList *list;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          for (list = paths; list; list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              filename = g_build_filename (list->data, full_basename, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              if (! list->next ||
							 | 
						||
| 
								 | 
							
								                  g_file_test (filename, G_FILE_TEST_EXISTS))
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_clear_pointer (&filename, g_free);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_list_free_full (paths, g_free);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          filename = g_build_filename (pika_data_directory (), "menus", full_basename, NULL);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_return_val_if_fail (filename != NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (manager->pika->be_verbose)
							 | 
						||
| 
								 | 
							
								        g_print ("Loading menu '%s' for %s\n",
							 | 
						||
| 
								 | 
							
								                 pika_filename_to_utf8 (filename), entry->ui_path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /* The model is owned by the builder which I have to keep around. */
							 | 
						||
| 
								 | 
							
								      entry->builder = pika_ui_manager_load_builder (filename, root);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_free (filename);
							 | 
						||
| 
								 | 
							
								      g_free (full_basename);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gmodel = G_MENU_MODEL (gtk_builder_get_object (entry->builder, root));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (G_IS_MENU (gmodel), NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model = pika_menu_model_new (manager, gmodel);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  submodel = pika_menu_model_get_submodel (model, submenus);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_object_unref (model);
							 | 
						||
| 
								 | 
							
								  g_free (root);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return submodel;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_remove_ui (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                           const gchar   *action_name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *iter = manager->ui_items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (action_name != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (; iter != NULL; iter = iter->next)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerMenuItem *item = iter->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (g_strcmp0 (item->action_name, action_name) == 0)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          g_signal_emit (manager, manager_signals[UI_REMOVED], 0,
							 | 
						||
| 
								 | 
							
								                         item->path, item->action_name);
							 | 
						||
| 
								 | 
							
								          manager->ui_items = g_list_delete_link (manager->ui_items, iter);
							 | 
						||
| 
								 | 
							
								          pika_ui_manager_menu_item_free (item);
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_remove_uis (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                            const gchar   *action_name_prefix)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *iter = manager->ui_items;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (iter != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerMenuItem *item         = iter->data;
							 | 
						||
| 
								 | 
							
								      GList                 *current_iter = iter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /* Increment nearly in case we delete the list item. */
							 | 
						||
| 
								 | 
							
								      iter = iter->next;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (action_name_prefix == NULL ||
							 | 
						||
| 
								 | 
							
								          g_str_has_prefix (item->action_name, action_name_prefix))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          g_signal_emit (manager, manager_signals[UI_REMOVED], 0,
							 | 
						||
| 
								 | 
							
								                         item->path, item->action_name);
							 | 
						||
| 
								 | 
							
								          manager->ui_items = g_list_delete_link (manager->ui_items,
							 | 
						||
| 
								 | 
							
								                                                  current_iter);
							 | 
						||
| 
								 | 
							
								          pika_ui_manager_menu_item_free (item);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_add_ui (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                        const gchar   *path,
							 | 
						||
| 
								 | 
							
								                        const gchar   *action_name,
							 | 
						||
| 
								 | 
							
								                        gboolean       top)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManagerMenuItem *item;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (path != NULL);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (action_name != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  item = g_slice_new0 (PikaUIManagerMenuItem);
							 | 
						||
| 
								 | 
							
								  item->path        = g_strdup (path);
							 | 
						||
| 
								 | 
							
								  item->action_name = g_strdup (action_name);
							 | 
						||
| 
								 | 
							
								  item->top         = top;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager->ui_items = g_list_prepend (manager->ui_items, item);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_signal_emit (manager, manager_signals[UI_ADDED], 0,
							 | 
						||
| 
								 | 
							
								                 path, action_name, top);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_foreach_ui (PikaUIManager      *manager,
							 | 
						||
| 
								 | 
							
								                            PikaUIMenuCallback  callback,
							 | 
						||
| 
								 | 
							
								                            gpointer            user_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  for (GList *iter = g_list_last (manager->ui_items); iter; iter = iter->prev)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerMenuItem *item = iter->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      callback (manager, item->path, item->action_name, item->top, user_data);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PikaAction *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_find_action (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                             const gchar   *group_name,
							 | 
						||
| 
								 | 
							
								                             const gchar   *action_name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaActionGroup *group;
							 | 
						||
| 
								 | 
							
								  PikaAction      *action = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), NULL);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (action_name != NULL, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (g_strcmp0 (group_name, "app") == 0)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GApplication *app = manager->pika->app;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      action = (PikaAction *) g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  else if (group_name)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      group = pika_ui_manager_get_action_group (manager, group_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (group)
							 | 
						||
| 
								 | 
							
								        action = pika_action_group_get_action (group, action_name);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GList *list;
							 | 
						||
| 
								 | 
							
								      gchar *dot;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      dot = strstr (action_name, ".");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (dot == NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          /* No group specified. */
							 | 
						||
| 
								 | 
							
								          for (list = pika_ui_manager_get_action_groups (manager);
							 | 
						||
| 
								 | 
							
								               list;
							 | 
						||
| 
								 | 
							
								               list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              group = list->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              action = pika_action_group_get_action (group, action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              if (action)
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gchar *gname;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          gname = g_strndup (action_name, dot - action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          action = pika_ui_manager_find_action (manager, gname, dot + 1);
							 | 
						||
| 
								 | 
							
								          g_free (gname);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return action;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								gboolean
							 | 
						||
| 
								 | 
							
								pika_ui_manager_activate_action (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                                 const gchar   *group_name,
							 | 
						||
| 
								 | 
							
								                                 const gchar   *action_name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaAction *action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), FALSE);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (action_name != NULL, FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  action = pika_ui_manager_find_action (manager, group_name, action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (action)
							 | 
						||
| 
								 | 
							
								    pika_action_activate (action);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return (action != NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								gboolean
							 | 
						||
| 
								 | 
							
								pika_ui_manager_toggle_action (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                               const gchar   *group_name,
							 | 
						||
| 
								 | 
							
								                               const gchar   *action_name,
							 | 
						||
| 
								 | 
							
								                               gboolean       active)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaAction *action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (PIKA_IS_UI_MANAGER (manager), FALSE);
							 | 
						||
| 
								 | 
							
								  g_return_val_if_fail (action_name != NULL, FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  action = pika_ui_manager_find_action (manager, group_name, action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (PIKA_IS_TOGGLE_ACTION (action))
							 | 
						||
| 
								 | 
							
								    pika_toggle_action_set_active (PIKA_TOGGLE_ACTION (action),
							 | 
						||
| 
								 | 
							
								                                   active ? TRUE : FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return PIKA_IS_TOGGLE_ACTION (action);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_ui_register (PikaUIManager          *manager,
							 | 
						||
| 
								 | 
							
								                             const gchar            *ui_path,
							 | 
						||
| 
								 | 
							
								                             const gchar            *basename,
							 | 
						||
| 
								 | 
							
								                             PikaUIManagerSetupFunc  setup_func)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManagerUIEntry *entry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (ui_path != NULL);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (basename != NULL);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (pika_ui_manager_entry_get (manager, ui_path) == NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  entry = g_slice_new0 (PikaUIManagerUIEntry);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  entry->ui_path    = g_strdup (ui_path);
							 | 
						||
| 
								 | 
							
								  entry->basename   = g_strdup (basename);
							 | 
						||
| 
								 | 
							
								  entry->setup_func = setup_func;
							 | 
						||
| 
								 | 
							
								  entry->setup_done = FALSE;
							 | 
						||
| 
								 | 
							
								  entry->builder    = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  manager->registered_uis = g_list_prepend (manager->registered_uis, entry);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_ui_popup_at_widget (PikaUIManager  *manager,
							 | 
						||
| 
								 | 
							
								                                    const gchar    *ui_path,
							 | 
						||
| 
								 | 
							
								                                    PikaUIManager  *child_ui_manager,
							 | 
						||
| 
								 | 
							
								                                    const gchar    *child_ui_path,
							 | 
						||
| 
								 | 
							
								                                    GtkWidget      *widget,
							 | 
						||
| 
								 | 
							
								                                    GdkGravity      widget_anchor,
							 | 
						||
| 
								 | 
							
								                                    GdkGravity      menu_anchor,
							 | 
						||
| 
								 | 
							
								                                    const GdkEvent *trigger_event,
							 | 
						||
| 
								 | 
							
								                                    GDestroyNotify  popdown_func,
							 | 
						||
| 
								 | 
							
								                                    gpointer        popdown_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model;
							 | 
						||
| 
								 | 
							
								  GtkWidget     *menu;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (ui_path != NULL);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (GTK_IS_WIDGET (widget));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  menu = pika_menu_new (manager);
							 | 
						||
| 
								 | 
							
								  gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model = pika_ui_manager_get_model (manager, ui_path);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (model != NULL);
							 | 
						||
| 
								 | 
							
								  pika_menu_shell_fill (PIKA_MENU_SHELL (menu), model, TRUE);
							 | 
						||
| 
								 | 
							
								  g_object_unref (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (! menu)
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (child_ui_manager != NULL && child_ui_path != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaMenuModel *child_model;
							 | 
						||
| 
								 | 
							
								      GtkWidget     *child_menu;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /* TODO GMenu: the "icon" attribute set in the .ui file should be visible. */
							 | 
						||
| 
								 | 
							
								      child_model = pika_ui_manager_get_model (child_ui_manager, child_ui_path);
							 | 
						||
| 
								 | 
							
								      child_menu  = pika_menu_new (child_ui_manager);
							 | 
						||
| 
								 | 
							
								      pika_menu_shell_fill (PIKA_MENU_SHELL (child_menu), child_model, FALSE);
							 | 
						||
| 
								 | 
							
								      g_object_unref (child_model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      pika_menu_merge (PIKA_MENU (menu), PIKA_MENU (child_menu), TRUE);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (popdown_func && popdown_data)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_object_set_data_full (G_OBJECT (manager), "popdown-data",
							 | 
						||
| 
								 | 
							
								                              popdown_data, popdown_func);
							 | 
						||
| 
								 | 
							
								      g_signal_connect (menu, "selection-done",
							 | 
						||
| 
								 | 
							
								                        G_CALLBACK (pika_ui_manager_delete_popdown_data),
							 | 
						||
| 
								 | 
							
								                        manager);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gtk_menu_popup_at_widget (GTK_MENU (menu), widget,
							 | 
						||
| 
								 | 
							
								                            widget_anchor,
							 | 
						||
| 
								 | 
							
								                            menu_anchor,
							 | 
						||
| 
								 | 
							
								                            trigger_event);
							 | 
						||
| 
								 | 
							
								  g_signal_connect (menu, "hide",
							 | 
						||
| 
								 | 
							
								                    G_CALLBACK (pika_ui_manager_popup_hidden),
							 | 
						||
| 
								 | 
							
								                    NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_ui_popup_at_pointer (PikaUIManager  *manager,
							 | 
						||
| 
								 | 
							
								                                     const gchar    *ui_path,
							 | 
						||
| 
								 | 
							
								                                     GtkWidget      *attached_widget,
							 | 
						||
| 
								 | 
							
								                                     const GdkEvent *trigger_event,
							 | 
						||
| 
								 | 
							
								                                     GDestroyNotify  popdown_func,
							 | 
						||
| 
								 | 
							
								                                     gpointer        popdown_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model;
							 | 
						||
| 
								 | 
							
								  GtkWidget     *menu;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (ui_path != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model = pika_ui_manager_get_model (manager, ui_path);
							 | 
						||
| 
								 | 
							
								  menu  = pika_menu_new (manager);
							 | 
						||
| 
								 | 
							
								  gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
							 | 
						||
| 
								 | 
							
								  pika_menu_shell_fill (PIKA_MENU_SHELL (menu), model, TRUE);
							 | 
						||
| 
								 | 
							
								  g_object_unref (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (! menu)
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (popdown_func && popdown_data)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_object_set_data_full (G_OBJECT (manager), "popdown-data",
							 | 
						||
| 
								 | 
							
								                              popdown_data, popdown_func);
							 | 
						||
| 
								 | 
							
								      g_signal_connect (menu, "selection-done",
							 | 
						||
| 
								 | 
							
								                        G_CALLBACK (pika_ui_manager_delete_popdown_data),
							 | 
						||
| 
								 | 
							
								                        manager);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
							 | 
						||
| 
								 | 
							
								  g_signal_connect (menu, "hide",
							 | 
						||
| 
								 | 
							
								                    G_CALLBACK (pika_ui_manager_popup_hidden),
							 | 
						||
| 
								 | 
							
								                    NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_ui_popup_at_rect (PikaUIManager      *manager,
							 | 
						||
| 
								 | 
							
								                                  const gchar        *ui_path,
							 | 
						||
| 
								 | 
							
								                                  GtkWidget          *attached_widget,
							 | 
						||
| 
								 | 
							
								                                  GdkWindow          *window,
							 | 
						||
| 
								 | 
							
								                                  const GdkRectangle *rect,
							 | 
						||
| 
								 | 
							
								                                  GdkGravity          rect_anchor,
							 | 
						||
| 
								 | 
							
								                                  GdkGravity          menu_anchor,
							 | 
						||
| 
								 | 
							
								                                  const GdkEvent     *trigger_event,
							 | 
						||
| 
								 | 
							
								                                  GDestroyNotify      popdown_func,
							 | 
						||
| 
								 | 
							
								                                  gpointer            popdown_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaMenuModel *model;
							 | 
						||
| 
								 | 
							
								  GtkWidget     *menu;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (ui_path != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model = pika_ui_manager_get_model (manager, ui_path);
							 | 
						||
| 
								 | 
							
								  menu  = pika_menu_new (manager);
							 | 
						||
| 
								 | 
							
								  gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
							 | 
						||
| 
								 | 
							
								  pika_menu_shell_fill (PIKA_MENU_SHELL (menu), model, TRUE);
							 | 
						||
| 
								 | 
							
								  g_object_unref (model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (! menu)
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (popdown_func && popdown_data)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_object_set_data_full (G_OBJECT (manager), "popdown-data",
							 | 
						||
| 
								 | 
							
								                              popdown_data, popdown_func);
							 | 
						||
| 
								 | 
							
								      g_signal_connect (menu, "selection-done",
							 | 
						||
| 
								 | 
							
								                        G_CALLBACK (pika_ui_manager_delete_popdown_data),
							 | 
						||
| 
								 | 
							
								                        manager);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gtk_menu_popup_at_rect (GTK_MENU (menu), window,
							 | 
						||
| 
								 | 
							
								                          rect, rect_anchor, menu_anchor,
							 | 
						||
| 
								 | 
							
								                          trigger_event);
							 | 
						||
| 
								 | 
							
								  g_signal_connect (menu, "hide",
							 | 
						||
| 
								 | 
							
								                    G_CALLBACK (pika_ui_manager_popup_hidden),
							 | 
						||
| 
								 | 
							
								                    NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*  private functions  */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static PikaUIManagerUIEntry *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_entry_get (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                           const gchar   *ui_path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GList *list;
							 | 
						||
| 
								 | 
							
								  gchar *path;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  path = g_strdup (ui_path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (strlen (path) > 1)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      gchar *p = strchr (path + 1, '/');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (p)
							 | 
						||
| 
								 | 
							
								        *p = '\0';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (list = manager->registered_uis; list; list = g_list_next (list))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      PikaUIManagerUIEntry *entry = list->data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (! strcmp (entry->ui_path, path))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          g_free (path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          return entry;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_free (path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return NULL;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static PikaUIManagerUIEntry *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_entry_ensure (PikaUIManager *manager,
							 | 
						||
| 
								 | 
							
								                              const gchar   *path)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaUIManagerUIEntry *entry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  entry = pika_ui_manager_entry_get (manager, path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (! entry)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_warning ("%s: no entry registered for \"%s\"", G_STRFUNC, path);
							 | 
						||
| 
								 | 
							
								      return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (entry->setup_func && ! entry->setup_done)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      entry->setup_func (manager, entry->ui_path);
							 | 
						||
| 
								 | 
							
								      entry->setup_done = TRUE;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return entry;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_store_builder_path (GMenuModel *model,
							 | 
						||
| 
								 | 
							
								                                    GMenuModel *original)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gint n_items = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (g_menu_model_get_n_items (model) == g_menu_model_get_n_items (original));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  n_items = (model != NULL ? g_menu_model_get_n_items (model) : 0);
							 | 
						||
| 
								 | 
							
								  for (gint i = 0; i < n_items; i++)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      GMenuModel *subsection;
							 | 
						||
| 
								 | 
							
								      GMenuModel *submenu;
							 | 
						||
| 
								 | 
							
								      GMenuModel *en_submodel = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      subsection = g_menu_model_get_item_link (G_MENU_MODEL (model), i, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								      submenu    = g_menu_model_get_item_link (G_MENU_MODEL (model), i, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								      if (subsection != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          en_submodel = g_menu_model_get_item_link (G_MENU_MODEL (original), i, G_MENU_LINK_SECTION);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          pika_ui_manager_store_builder_path (subsection, en_submodel);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      else if (submenu != NULL)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								          gchar *label = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          en_submodel = g_menu_model_get_item_link (G_MENU_MODEL (original),
							 | 
						||
| 
								 | 
							
								                                                    i, G_MENU_LINK_SUBMENU);
							 | 
						||
| 
								 | 
							
								          pika_ui_manager_store_builder_path (submenu, en_submodel);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_menu_model_get_item_attribute (G_MENU_MODEL (model), i,
							 | 
						||
| 
								 | 
							
								                                           G_MENU_ATTRIBUTE_LABEL, "s", &label);
							 | 
						||
| 
								 | 
							
								          if (label != NULL)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								              gchar *en_label = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_menu_model_get_item_attribute (G_MENU_MODEL (original), i,
							 | 
						||
| 
								 | 
							
								                                               G_MENU_ATTRIBUTE_LABEL, "s", &en_label);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								              g_object_set_data_full (G_OBJECT (submenu), "pika-ui-manager-menu-model-en-label",
							 | 
						||
| 
								 | 
							
								                                      en_label, (GDestroyNotify) g_free);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          g_free (label);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_clear_object (&submenu);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&subsection);
							 | 
						||
| 
								 | 
							
								      g_clear_object (&en_submodel);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static GtkBuilder *
							 | 
						||
| 
								 | 
							
								pika_ui_manager_load_builder (const gchar *filename,
							 | 
						||
| 
								 | 
							
								                              const gchar *root)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  const GMarkupParser markup_parser =
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      pika_ui_manager_parse_start_element,
							 | 
						||
| 
								 | 
							
								      pika_ui_manager_parse_end_element,
							 | 
						||
| 
								 | 
							
								      pika_ui_manager_parse_ui_text,
							 | 
						||
| 
								 | 
							
								      NULL,  /*  passthrough  */
							 | 
						||
| 
								 | 
							
								      NULL   /*  error        */
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  GtkBuilder    *builder;
							 | 
						||
| 
								 | 
							
								  GtkBuilder    *en_builder;
							 | 
						||
| 
								 | 
							
								  gchar         *contents;
							 | 
						||
| 
								 | 
							
								  PikaXmlParser *xml_parser;
							 | 
						||
| 
								 | 
							
								  GString       *new_xml;
							 | 
						||
| 
								 | 
							
								  GMenuModel    *model;
							 | 
						||
| 
								 | 
							
								  GMenuModel    *en_model;
							 | 
						||
| 
								 | 
							
								  GError        *error = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /* First load the localized version. */
							 | 
						||
| 
								 | 
							
								  if (! g_file_get_contents (filename, &contents, NULL, &error))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_warning ("%s: failed to read \"%s\"", G_STRFUNC, filename);
							 | 
						||
| 
								 | 
							
								      g_clear_error (&error);
							 | 
						||
| 
								 | 
							
								      return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  builder = gtk_builder_new_from_string (contents, -1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /* Now transform the source XML as non-translatable and load it again. */
							 | 
						||
| 
								 | 
							
								  new_xml = g_string_new (NULL);
							 | 
						||
| 
								 | 
							
								  xml_parser = pika_xml_parser_new (&markup_parser, new_xml);
							 | 
						||
| 
								 | 
							
								  pika_xml_parser_parse_buffer (xml_parser, contents, -1, &error);
							 | 
						||
| 
								 | 
							
								  pika_xml_parser_free (xml_parser);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_free (contents);
							 | 
						||
| 
								 | 
							
								  contents = g_string_free (new_xml, FALSE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (error != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_warning ("%s: error parsing XML file \"%s\"", G_STRFUNC, filename);
							 | 
						||
| 
								 | 
							
								      g_clear_error (&error);
							 | 
						||
| 
								 | 
							
								      g_free (contents);
							 | 
						||
| 
								 | 
							
								      return builder;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  en_builder = gtk_builder_new_from_string (contents, -1);
							 | 
						||
| 
								 | 
							
								  g_free (contents);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  model    = G_MENU_MODEL (gtk_builder_get_object (builder, root));
							 | 
						||
| 
								 | 
							
								  en_model = G_MENU_MODEL (gtk_builder_get_object (en_builder, root));
							 | 
						||
| 
								 | 
							
								  pika_ui_manager_store_builder_path (model, en_model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_clear_object (&en_builder);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return builder;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_parse_start_element (GMarkupParseContext *context,
							 | 
						||
| 
								 | 
							
								                                     const gchar         *element_name,
							 | 
						||
| 
								 | 
							
								                                     const gchar        **attribute_names,
							 | 
						||
| 
								 | 
							
								                                     const gchar        **attribute_values,
							 | 
						||
| 
								 | 
							
								                                     gpointer             user_data,
							 | 
						||
| 
								 | 
							
								                                     GError             **error)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GString *new_xml = user_data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_string_append_printf (new_xml, "<%s", element_name);
							 | 
						||
| 
								 | 
							
								  for (gint i = 0; attribute_names[i] != NULL; i++)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      if (g_strcmp0 (attribute_names[i], "translatable") == 0 ||
							 | 
						||
| 
								 | 
							
								          g_strcmp0 (attribute_names[i], "context") == 0)
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_string_append_printf (new_xml, " %s=\"%s\"",
							 | 
						||
| 
								 | 
							
								                              attribute_names[i], attribute_values[i]);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  g_string_append_printf (new_xml, ">");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_parse_end_element (GMarkupParseContext *context,
							 | 
						||
| 
								 | 
							
								                                   const gchar         *element_name,
							 | 
						||
| 
								 | 
							
								                                   gpointer             user_data,
							 | 
						||
| 
								 | 
							
								                                   GError             **error)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GString *new_xml = user_data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_string_append_printf (new_xml, "</%s>", element_name);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_parse_ui_text (GMarkupParseContext *context,
							 | 
						||
| 
								 | 
							
								                               const gchar         *text,
							 | 
						||
| 
								 | 
							
								                               gsize                text_len,
							 | 
						||
| 
								 | 
							
								                               gpointer             user_data,
							 | 
						||
| 
								 | 
							
								                               GError             **error)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GString *new_xml   = user_data;
							 | 
						||
| 
								 | 
							
								  gchar   *unchanged = g_markup_escape_text (text, text_len);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_string_append (new_xml, unchanged);
							 | 
						||
| 
								 | 
							
								  g_free (unchanged);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_delete_popdown_data (GtkWidget     *widget,
							 | 
						||
| 
								 | 
							
								                                     PikaUIManager *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_signal_handlers_disconnect_by_func (widget,
							 | 
						||
| 
								 | 
							
								                                        pika_ui_manager_delete_popdown_data,
							 | 
						||
| 
								 | 
							
								                                        manager);
							 | 
						||
| 
								 | 
							
								  g_object_set_data (G_OBJECT (manager), "popdown-data", NULL);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_menu_item_free (PikaUIManagerMenuItem *item)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  g_free (item->path);
							 | 
						||
| 
								 | 
							
								  g_free (item->action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_slice_free (PikaUIManagerMenuItem, item);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_popup_hidden (GtkMenuShell *popup,
							 | 
						||
| 
								 | 
							
								                              gpointer      user_data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  /* Destroying the popup would happen after we finish all other events related
							 | 
						||
| 
								 | 
							
								   * to this popup. Otherwise the action which might have been activated will
							 | 
						||
| 
								 | 
							
								   * not run because it happens after hiding.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  g_idle_add ((GSourceFunc) pika_ui_manager_popup_destroy, g_object_ref (popup));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static gboolean
							 | 
						||
| 
								 | 
							
								pika_ui_manager_popup_destroy (GtkWidget *popup)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gtk_menu_detach (GTK_MENU (popup));
							 | 
						||
| 
								 | 
							
								  g_object_unref (popup);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return G_SOURCE_REMOVE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_image_action_added (PikaActionGroup *group,
							 | 
						||
| 
								 | 
							
								                                    gchar           *action_name,
							 | 
						||
| 
								 | 
							
								                                    PikaUIManager   *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  PikaAction   *action;
							 | 
						||
| 
								 | 
							
								  const gchar **accels;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (PIKA_IS_UI_MANAGER (manager));
							 | 
						||
| 
								 | 
							
								  /* The action should not already exist in the application. */
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (g_action_map_lookup_action (G_ACTION_MAP (manager->pika->app), action_name) == NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  action = pika_action_group_get_action (group, action_name);
							 | 
						||
| 
								 | 
							
								  g_return_if_fail (action != NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  g_action_map_add_action (G_ACTION_MAP (manager->pika->app), G_ACTION (action));
							 | 
						||
| 
								 | 
							
								  g_signal_connect_object (action, "accels-changed",
							 | 
						||
| 
								 | 
							
								                           G_CALLBACK (pika_ui_manager_image_accels_changed),
							 | 
						||
| 
								 | 
							
								                           manager, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  accels = pika_action_get_accels (action);
							 | 
						||
| 
								 | 
							
								  if (accels && g_strv_length ((gchar **) accels) > 0)
							 | 
						||
| 
								 | 
							
								    pika_ui_manager_image_accels_changed (action, pika_action_get_accels (action), manager);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_image_action_removed (PikaActionGroup *group,
							 | 
						||
| 
								 | 
							
								                                      gchar           *action_name,
							 | 
						||
| 
								 | 
							
								                                      PikaUIManager   *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  GAction *action;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  action = g_action_map_lookup_action (G_ACTION_MAP (manager->pika->app), action_name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (action != NULL)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      g_action_map_remove_action (G_ACTION_MAP (manager->pika->app), action_name);
							 | 
						||
| 
								 | 
							
								      g_signal_handlers_disconnect_by_func (action,
							 | 
						||
| 
								 | 
							
								                                            G_CALLBACK (pika_ui_manager_image_accels_changed),
							 | 
						||
| 
								 | 
							
								                                            manager);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								pika_ui_manager_image_accels_changed (PikaAction     *action,
							 | 
						||
| 
								 | 
							
								                                      const gchar   **accels,
							 | 
						||
| 
								 | 
							
								                                      PikaUIManager  *manager)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  gchar *detailed_action_name;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (PIKA_IS_RADIO_ACTION (action))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      gint value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      g_object_get ((GObject *) action,
							 | 
						||
| 
								 | 
							
								                    "value", &value,
							 | 
						||
| 
								 | 
							
								                    NULL);
							 | 
						||
| 
								 | 
							
								      detailed_action_name = g_strdup_printf ("app.%s(%i)",
							 | 
						||
| 
								 | 
							
								                                               g_action_get_name (G_ACTION (action)),
							 | 
						||
| 
								 | 
							
								                                               value);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								      detailed_action_name = g_strdup_printf ("app.%s",
							 | 
						||
| 
								 | 
							
								                                               g_action_get_name (G_ACTION (action)));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  gtk_application_set_accels_for_action (GTK_APPLICATION (manager->pika->app),
							 | 
						||
| 
								 | 
							
								                                         detailed_action_name,
							 | 
						||
| 
								 | 
							
								                                         accels ? accels : (const gchar *[]) { NULL });
							 | 
						||
| 
								 | 
							
								  g_free (detailed_action_name);
							 | 
						||
| 
								 | 
							
								}
							 |