diff --git a/app/display/gimpimagewindow.c b/app/display/gimpimagewindow.c
index 19677a8777..70fe8ae814 100644
--- a/app/display/gimpimagewindow.c
+++ b/app/display/gimpimagewindow.c
@@ -348,7 +348,7 @@ gimp_image_window_constructed (GObject *object)
GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window);
GimpMenuFactory *menu_factory;
GimpGuiConfig *config;
- GMenuModel *model;
+ GimpMenuModel *model;
G_OBJECT_CLASS (parent_class)->constructed (object);
@@ -386,8 +386,7 @@ gimp_image_window_constructed (GObject *object)
gtk_widget_show (private->main_vbox);
/* Create the menubar */
- model = gimp_ui_manager_get_model (private->menubar_manager,
- "/image-menubar");
+ model = gimp_ui_manager_get_model (private->menubar_manager, "/image-menubar");
#ifdef GDK_WINDOWING_QUARTZ
/* macOS has its native menubar system, and this should support it. It means
@@ -395,7 +394,8 @@ gimp_image_window_constructed (GObject *object)
* TODO: Since the .ui file has no title/labels, I should edit the model to
* extract titles from actions.
*/
- gtk_application_set_menubar (private->gimp->app, G_MENU_MODEL (model));
+ gtk_application_set_menubar (GTK_APPLICATION (private->gimp->app),
+ G_MENU_MODEL (model));
#else
private->menubar = gimp_menu_bar_new (model, private->menubar_manager);
g_object_unref (model);
diff --git a/app/widgets/gimpaction.c b/app/widgets/gimpaction.c
index 31a5349cea..17b226d4f9 100644
--- a/app/widgets/gimpaction.c
+++ b/app/widgets/gimpaction.c
@@ -428,6 +428,7 @@ gimp_action_set_sensitive (GimpAction *action,
gimp_action_update_proxy_tooltip (action, NULL);
}
+ g_object_notify (G_OBJECT (action), "enabled");
}
gboolean
diff --git a/app/widgets/gimpmenu.c b/app/widgets/gimpmenu.c
index e96f69e11a..ada0e5c50a 100644
--- a/app/widgets/gimpmenu.c
+++ b/app/widgets/gimpmenu.c
@@ -33,6 +33,7 @@
#include "gimpenumaction.h"
#include "gimphelp-ids.h"
#include "gimpmenu.h"
+#include "gimpmenumodel.h"
#include "gimpmenushell.h"
#include "gimpprocedureaction.h"
#include "gimpradioaction.h"
@@ -69,7 +70,7 @@ static void gimp_menu_iface_init (GimpMenuShellInterface *ifac
static void gimp_menu_finalize (GObject *object);
static void gimp_menu_append (GimpMenuShell *shell,
- GMenuModel *model);
+ GimpMenuModel *model);
static void gimp_menu_add_ui (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name,
@@ -170,7 +171,7 @@ gimp_menu_finalize (GObject *object)
static void
gimp_menu_append (GimpMenuShell *shell,
- GMenuModel *model)
+ GimpMenuModel *model)
{
static GtkRadioMenuItem *group = NULL;
GimpMenu *menu = GIMP_MENU (shell);
@@ -179,7 +180,7 @@ gimp_menu_append (GimpMenuShell *shell,
g_return_if_fail (GTK_IS_CONTAINER (shell));
- n_items = g_menu_model_get_n_items (model);
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (model));
for (gint i = 0; i < n_items; i++)
{
GMenuModel *subsection;
@@ -187,10 +188,10 @@ gimp_menu_append (GimpMenuShell *shell,
gchar *label = NULL;
gchar *action_name = NULL;
- subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
- submenu = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
+ 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);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
if (subsection != NULL)
{
@@ -202,7 +203,7 @@ gimp_menu_append (GimpMenuShell *shell,
gtk_container_add (GTK_CONTAINER (shell), item);
gtk_widget_show (item);
- gimp_menu_append (shell, subsection);
+ gimp_menu_append (shell, GIMP_MENU_MODEL (subsection));
item = gtk_separator_menu_item_new ();
gtk_container_add (GTK_CONTAINER (shell), item);
@@ -240,7 +241,7 @@ gimp_menu_append (GimpMenuShell *shell,
subcontainer = gimp_menu_new (manager);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
- gimp_menu_append (GIMP_MENU_SHELL (subcontainer), submenu);
+ gimp_menu_append (GIMP_MENU_SHELL (subcontainer), GIMP_MENU_MODEL (submenu));
gtk_widget_show (subcontainer);
g_tree_insert (menu->priv->submenus,
@@ -263,7 +264,7 @@ gimp_menu_append (GimpMenuShell *shell,
subcontainer = gimp_menu_new (manager);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
- gimp_menu_append (GIMP_MENU_SHELL (subcontainer), submenu);
+ gimp_menu_append (GIMP_MENU_SHELL (subcontainer), GIMP_MENU_MODEL (submenu));
gtk_widget_show (subcontainer);
g_tree_insert (menu->priv->submenus,
diff --git a/app/widgets/gimpmenubar.c b/app/widgets/gimpmenubar.c
index cde75cccd3..9e34dd3753 100644
--- a/app/widgets/gimpmenubar.c
+++ b/app/widgets/gimpmenubar.c
@@ -32,6 +32,7 @@
#include "gimpaction.h"
#include "gimpmenu.h"
#include "gimpmenubar.h"
+#include "gimpmenumodel.h"
#include "gimpmenushell.h"
#include "gimpradioaction.h"
#include "gimpuimanager.h"
@@ -57,9 +58,7 @@ enum
struct _GimpMenuBarPrivate
{
- GMenuModel *model;
-
- GTree *menus;
+ GTree *menus;
};
@@ -67,19 +66,10 @@ struct _GimpMenuBarPrivate
static void gimp_menu_bar_iface_init (GimpMenuShellInterface *iface);
-static void gimp_menu_bar_constructed (GObject *object);
static void gimp_menu_bar_dispose (GObject *object);
-static void gimp_menu_bar_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gimp_menu_bar_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec);
static void gimp_menu_bar_append (GimpMenuShell *shell,
- GMenuModel *model);
+ GimpMenuModel *model);
static void gimp_menu_bar_add_ui (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name,
@@ -101,19 +91,11 @@ gimp_menu_bar_class_init (GimpMenuBarClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->constructed = gimp_menu_bar_constructed;
object_class->dispose = gimp_menu_bar_dispose;
- object_class->get_property = gimp_menu_bar_get_property;
- object_class->set_property = gimp_menu_bar_set_property;
+ object_class->get_property = gimp_menu_shell_get_property;
+ object_class->set_property = gimp_menu_shell_set_property;
gimp_menu_shell_install_properties (object_class);
-
- g_object_class_install_property (object_class, PROP_MODEL,
- g_param_spec_object ("model",
- NULL, NULL,
- G_TYPE_MENU_MODEL,
- GIMP_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
}
static void
@@ -134,67 +116,19 @@ gimp_menu_bar_init (GimpMenuBar *bar)
gimp_menu_shell_init (GIMP_MENU_SHELL (bar));
}
-static void
-gimp_menu_bar_constructed (GObject *object)
-{
- G_OBJECT_CLASS (parent_class)->constructed (object);
-}
-
static void
gimp_menu_bar_dispose (GObject *object)
{
GimpMenuBar *bar = GIMP_MENU_BAR (object);
- g_clear_object (&bar->priv->model);
g_clear_pointer (&bar->priv->menus, g_tree_unref);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
-static void
-gimp_menu_bar_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GimpMenuBar *bar = GIMP_MENU_BAR (object);
-
- switch (property_id)
- {
- case PROP_MODEL:
- bar->priv->model = g_value_dup_object (value);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (bar), bar->priv->model, "ui-added", FALSE);
- break;
-
- default:
- gimp_menu_shell_set_property (object, property_id, value, pspec);
- break;
- }
-}
-
-static void
-gimp_menu_bar_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GimpMenuBar *bar = GIMP_MENU_BAR (object);
-
- switch (property_id)
- {
- case PROP_MODEL:
- g_value_set_object (value, bar->priv->model);
- break;
-
- default:
- gimp_menu_shell_get_property (object, property_id, value, pspec);
- break;
- }
-}
-
static void
gimp_menu_bar_append (GimpMenuShell *shell,
- GMenuModel *model)
+ GimpMenuModel *model)
{
GimpMenuBar *bar = GIMP_MENU_BAR (shell);
GimpUIManager *manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (shell));
@@ -202,7 +136,7 @@ gimp_menu_bar_append (GimpMenuShell *shell,
g_return_if_fail (GTK_IS_CONTAINER (shell));
- n_items = g_menu_model_get_n_items (model);
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (model));
for (gint i = 0; i < n_items; i++)
{
GMenuModel *subsection;
@@ -210,10 +144,10 @@ gimp_menu_bar_append (GimpMenuShell *shell,
gchar *label = NULL;
gchar *action_name = NULL;
- subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
- submenu = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
+ 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);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
if (submenu != NULL)
{
@@ -231,7 +165,7 @@ gimp_menu_bar_append (GimpMenuShell *shell,
subcontainer = gimp_menu_new (manager);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
- GIMP_MENU_SHELL_GET_INTERFACE (subcontainer)->append (GIMP_MENU_SHELL (subcontainer), submenu);
+ gimp_menu_shell_append (GIMP_MENU_SHELL (subcontainer), GIMP_MENU_MODEL (submenu));
gtk_widget_show (subcontainer);
g_tree_insert (bar->priv->menus,
@@ -305,7 +239,7 @@ gimp_menu_bar_add_ui (GimpMenuShell *shell,
/* Public functions */
GtkWidget *
-gimp_menu_bar_new (GMenuModel *model,
+gimp_menu_bar_new (GimpMenuModel *model,
GimpUIManager *manager)
{
g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager) &&
diff --git a/app/widgets/gimpmenubar.h b/app/widgets/gimpmenubar.h
index f255914e20..40e699f758 100644
--- a/app/widgets/gimpmenubar.h
+++ b/app/widgets/gimpmenubar.h
@@ -48,7 +48,7 @@ struct _GimpMenuBarClass
GType gimp_menu_bar_get_type (void) G_GNUC_CONST;
-GtkWidget * gimp_menu_bar_new (GMenuModel *model,
+GtkWidget * gimp_menu_bar_new (GimpMenuModel *model,
GimpUIManager *manager);
diff --git a/app/widgets/gimpmenumodel.c b/app/widgets/gimpmenumodel.c
new file mode 100644
index 0000000000..d6e522db3e
--- /dev/null
+++ b/app/widgets/gimpmenumodel.c
@@ -0,0 +1,865 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpmenu_model.c
+ * Copyright (C) 2023 Jehan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "config.h"
+
+#include
+#include
+
+#include "widgets-types.h"
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "core/gimp.h"
+
+#include "gimpaction.h"
+#include "gimpmenumodel.h"
+#include "gimpmenushell.h"
+#include "gimpuimanager.h"
+
+
+/**
+ * GimpMenuModel:
+ *
+ * GimpMenuModel implements GMenuModel. We initialize an object from another
+ * GMenuModel (usually a GMenu), auto-fill with various data from the
+ * GimpAction, when they are not in GAction API, e.g. labels, but action
+ * visibility.
+ *
+ * The object will also synchronize automatically with changes from the actions,
+ * but also GimpUIManager for dynamic contents and will trigger an
+ * "items-changed" when necessary. This allows for such variant to be used in
+ * GTK API which has no knowledge of GimpAction or GimpUIManager enhancements.
+ */
+
+
+enum
+{
+ PROP_0,
+ PROP_MANAGER,
+ PROP_MODEL,
+ PROP_PATH,
+ PROP_IS_SECTION,
+};
+
+struct _GimpMenuModelPrivate
+{
+ GimpUIManager *manager;
+ GMenuModel *model;
+
+ gchar *path;
+ gboolean is_section;
+
+ GList *items;
+ gint last_n_items;
+};
+
+
+static void gimp_menu_model_finalize (GObject *object);
+static void gimp_menu_model_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_menu_model_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static GVariant * gimp_menu_model_get_item_attribute_value (GMenuModel *model,
+ gint item_index,
+ const gchar *attribute,
+ const GVariantType *expected_type);
+static void gimp_menu_model_get_item_attributes (GMenuModel *model,
+ gint item_index,
+ GHashTable **attributes);
+static GMenuModel * gimp_menu_model_get_item_link (GMenuModel *model,
+ gint item_index,
+ const gchar *link);
+static void gimp_menu_model_get_item_links (GMenuModel *model,
+ gint item_index,
+ GHashTable **links);
+static gint gimp_menu_model_get_n_items (GMenuModel *model);
+static gboolean gimp_menu_model_is_mutable (GMenuModel *model);
+
+static void gimp_menu_model_initialize (GimpMenuModel *model,
+ GMenuModel *gmodel);
+static void gimp_menu_model_update (GimpMenuModel *model);
+
+static GMenuItem * gimp_menu_model_get_item (GimpMenuModel *model,
+ gint idx);
+
+static void gimp_menu_model_action_notify_visible (GimpAction *action,
+ GParamSpec *pspec,
+ GimpMenuModel *model);
+static void gimp_menu_model_action_notify_label (GimpAction *action,
+ GParamSpec *pspec,
+ GMenuItem *item);
+
+static gboolean gimp_menu_model_ui_added (GimpUIManager *manager,
+ const gchar *path,
+ const gchar *action_name,
+ const gchar *placeholder_key,
+ gboolean top,
+ GimpMenuModel *model);
+static gboolean gimp_menu_model_ui_removed (GimpUIManager *manager,
+ const gchar *path,
+ const gchar *action_name,
+ GimpMenuModel *model);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpMenuModel, gimp_menu_model, G_TYPE_MENU_MODEL, G_ADD_PRIVATE (GimpMenuModel))
+
+#define parent_class gimp_menu_model_parent_class
+
+
+/* Class functions */
+
+static void
+gimp_menu_model_class_init (GimpMenuModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GMenuModelClass *model_class = G_MENU_MODEL_CLASS (klass);
+
+ object_class->finalize = gimp_menu_model_finalize;
+ object_class->get_property = gimp_menu_model_get_property;
+ object_class->set_property = gimp_menu_model_set_property;
+
+ model_class->get_item_attribute_value = gimp_menu_model_get_item_attribute_value;
+ model_class->get_item_attributes = gimp_menu_model_get_item_attributes;
+ model_class->get_item_link = gimp_menu_model_get_item_link;
+ model_class->get_item_links = gimp_menu_model_get_item_links;
+ model_class->get_n_items = gimp_menu_model_get_n_items;
+ model_class->is_mutable = gimp_menu_model_is_mutable;
+
+ g_object_class_install_property (object_class, PROP_MANAGER,
+ g_param_spec_object ("manager",
+ NULL, NULL,
+ GIMP_TYPE_UI_MANAGER,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class, PROP_MODEL,
+ g_param_spec_object ("model",
+ NULL, NULL,
+ G_TYPE_MENU_MODEL,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_PATH,
+ g_param_spec_string ("path",
+ NULL, NULL, NULL,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (object_class, PROP_IS_SECTION,
+ g_param_spec_boolean ("section",
+ NULL, NULL, FALSE,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_menu_model_init (GimpMenuModel *model)
+{
+ model->priv = gimp_menu_model_get_instance_private (model);
+
+ model->priv->items = NULL;
+ model->priv->last_n_items = 0;
+ model->priv->path = 0;
+ model->priv->is_section = FALSE;
+}
+
+static void
+gimp_menu_model_finalize (GObject *object)
+{
+ GimpMenuModel *model = GIMP_MENU_MODEL (object);
+
+ g_clear_object (&model->priv->model);
+ g_list_free_full (model->priv->items, g_object_unref);
+ g_free (model->priv->path);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+void
+gimp_menu_model_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpMenuModel *model = GIMP_MENU_MODEL (object);
+
+ switch (property_id)
+ {
+ case PROP_MANAGER:
+ g_value_set_object (value, model->priv->manager);
+ break;
+ case PROP_MODEL:
+ g_value_set_object (value, model->priv->model);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+void
+gimp_menu_model_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpMenuModel *model = GIMP_MENU_MODEL (object);
+
+ switch (property_id)
+ {
+ case PROP_MANAGER:
+ g_set_weak_pointer (&model->priv->manager, g_value_get_object (value));
+ break;
+ case PROP_MODEL:
+ model->priv->model = g_value_dup_object (value);
+ gimp_menu_model_initialize (model, model->priv->model);
+ break;
+ case PROP_PATH:
+ model->priv->path = g_value_dup_string (value);
+ break;
+ case PROP_IS_SECTION:
+ model->priv->is_section = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GVariant*
+gimp_menu_model_get_item_attribute_value (GMenuModel *model,
+ gint item_index,
+ const gchar *attribute,
+ const GVariantType *expected_type)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GMenuItem *item;
+
+ item = gimp_menu_model_get_item (m, item_index);
+
+ return g_menu_item_get_attribute_value (item, attribute, expected_type);
+}
+
+static void
+gimp_menu_model_get_item_attributes (GMenuModel *model,
+ gint item_index,
+ GHashTable **attributes)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GMenuItem *item;
+ GVariant *value;
+
+ item = gimp_menu_model_get_item (m, item_index);
+
+ *attributes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify) g_variant_unref);
+
+ value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_LABEL, NULL);
+ if (value)
+ g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_LABEL, value);
+
+ value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, NULL);
+ if (value)
+ g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ACTION, value);
+
+ value = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ICON, NULL);
+ if (value)
+ g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ICON, value);
+
+ value = g_menu_item_get_attribute_value (item, G_MENU_LINK_SUBMENU, NULL);
+ if (value)
+ g_hash_table_insert (*attributes, G_MENU_LINK_SUBMENU, value);
+
+ value = g_menu_item_get_attribute_value (item, G_MENU_LINK_SECTION, NULL);
+ if (value)
+ g_hash_table_insert (*attributes, G_MENU_LINK_SECTION, value);
+}
+
+static GMenuModel*
+gimp_menu_model_get_item_link (GMenuModel *model,
+ gint item_index,
+ const gchar *link)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GMenuItem *item;
+
+ item = gimp_menu_model_get_item (m, item_index);
+
+ return g_menu_item_get_link (item, link);
+}
+
+
+static void
+gimp_menu_model_get_item_links (GMenuModel *model,
+ gint item_index,
+ GHashTable **links)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GMenuModel *subsection;
+ GMenuModel *submenu;
+ GMenuItem *item;
+
+ *links = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify) g_object_unref);
+
+ item = gimp_menu_model_get_item (m, item_index);
+ subsection = g_menu_item_get_link (item, G_MENU_LINK_SECTION);
+ submenu = g_menu_item_get_link (item, G_MENU_LINK_SUBMENU);
+
+ if (subsection)
+ g_hash_table_insert (*links, G_MENU_LINK_SECTION, g_object_ref (subsection));
+ if (submenu)
+ g_hash_table_insert (*links, G_MENU_LINK_SUBMENU, g_object_ref (submenu));
+
+ g_clear_object (&subsection);
+ g_clear_object (&submenu);
+}
+
+static gint
+gimp_menu_model_get_n_items (GMenuModel *model)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GApplication *app = m->priv->manager->gimp->app;
+ gint len = 0;
+
+ for (GList *iter = m->priv->items; iter; iter = iter->next)
+ {
+ const gchar *action_name = NULL;
+ GMenuModel *subsection;
+ GMenuModel *submenu;
+
+ subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
+ submenu = g_menu_item_get_link (iter->data, G_MENU_LINK_SUBMENU);
+
+ if (subsection || submenu)
+ {
+ len++;
+ }
+ /* Count neither placeholders (items with no action_name), nor invisible
+ * actions.
+ */
+ else if (g_menu_item_get_attribute (iter->data,
+ G_MENU_ATTRIBUTE_ACTION,
+ "&s", &action_name))
+ {
+ GAction *action;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (app),
+ action_name + 4);
+ if (gimp_action_is_visible (GIMP_ACTION (action)))
+ len++;
+ }
+ g_clear_object (&subsection);
+ g_clear_object (&submenu);
+ }
+
+ m->priv->last_n_items = len;
+
+ return len;
+}
+
+static gboolean
+gimp_menu_model_is_mutable (GMenuModel* model)
+{
+ return TRUE;
+}
+
+
+/* Public functions. */
+
+GimpMenuModel *
+gimp_menu_model_new (GimpUIManager *manager,
+ GMenuModel *model)
+{
+ g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);
+
+ return g_object_new (GIMP_TYPE_MENU_MODEL,
+ "manager", manager,
+ "model", model,
+ NULL);
+}
+
+GimpMenuModel *
+gimp_menu_model_get_submodel (GimpMenuModel *model,
+ const gchar *path)
+{
+ GimpMenuModel *submodel;
+ gchar *submenus;
+ gchar *submenu;
+ gchar *subsubmenus;
+
+ submodel = g_object_ref (model);
+
+ if (path == NULL)
+ return submodel;
+
+ submenus = g_strdup (path);
+ subsubmenus = submenus;
+
+ while (subsubmenus && strlen (subsubmenus) > 0)
+ {
+ gint n_items;
+ gint i;
+
+ submenu = subsubmenus;
+ while (*submenu == '/')
+ submenu++;
+
+ subsubmenus = strstr (submenu, "/");
+ if (subsubmenus)
+ *(subsubmenus++) = '\0';
+
+ if (strlen (submenu) == 0)
+ break;
+
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (submodel));
+ for (i = 0; i < n_items; i++)
+ {
+ GMenuModel *subsubmodel;
+ gchar *label = NULL;
+ gchar *canon_label = NULL;
+
+ subsubmodel = g_menu_model_get_item_link (G_MENU_MODEL (submodel), i, G_MENU_LINK_SUBMENU);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (submodel), i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ if (label)
+ canon_label = gimp_menu_shell_make_canonical_path (label);
+
+ if (subsubmodel && g_strcmp0 (canon_label, submenu) == 0)
+ {
+ g_object_unref (submodel);
+ submodel = GIMP_MENU_MODEL (subsubmodel);
+
+ g_free (label);
+ g_free (canon_label);
+ break;
+ }
+ g_clear_object (&subsubmodel);
+ g_free (label);
+ g_free (canon_label);
+ }
+ g_return_val_if_fail (i < n_items, NULL);
+ }
+
+ g_free (submenus);
+
+ return submodel;
+}
+
+const gchar *
+gimp_menu_model_get_path (GimpMenuModel *model)
+{
+ return model->priv->path;
+}
+
+
+/* Private functions. */
+
+static GimpMenuModel *
+gimp_menu_model_new_section (GimpUIManager *manager,
+ GMenuModel *model,
+ const gchar *path)
+{
+ g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);
+
+ return g_object_new (GIMP_TYPE_MENU_MODEL,
+ "manager", manager,
+ "model", model,
+ "path", path,
+ "section", TRUE,
+ NULL);
+}
+
+static GimpMenuModel *
+gimp_menu_model_new_submenu (GimpUIManager *manager,
+ GMenuModel *model,
+ const gchar *path)
+{
+ g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);
+
+ return g_object_new (GIMP_TYPE_MENU_MODEL,
+ "manager", manager,
+ "model", model,
+ "path", path,
+ NULL);
+}
+
+static void
+gimp_menu_model_initialize (GimpMenuModel *model,
+ GMenuModel *gmodel)
+{
+ GApplication *app = model->priv->manager->gimp->app;
+ gint n_items;
+
+ g_return_if_fail (GIMP_IS_MENU_MODEL (model));
+ g_return_if_fail (G_IS_MENU_MODEL (gmodel));
+
+ n_items = g_menu_model_get_n_items (gmodel);
+ for (int i = 0; i < n_items; i++)
+ {
+ GMenuModel *subsection;
+ GMenuModel *submenu;
+ gchar *label = NULL;
+ gchar *action_name = NULL;
+ GMenuItem *item = NULL;
+
+ subsection = g_menu_model_get_item_link (G_MENU_MODEL (gmodel), i, G_MENU_LINK_SECTION);
+ submenu = g_menu_model_get_item_link (G_MENU_MODEL (gmodel), i, G_MENU_LINK_SUBMENU);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (gmodel), i,
+ G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (gmodel), i,
+ G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
+
+ if (subsection != NULL)
+ {
+ GimpMenuModel *submodel;
+
+ submodel = gimp_menu_model_new_section (model->priv->manager, subsection,
+ model->priv->path);
+ item = g_menu_item_new_section (label, G_MENU_MODEL (submodel));
+ g_object_unref (submodel);
+ }
+ else if (submenu != NULL)
+ {
+ GimpMenuModel *submodel;
+ gchar *canon_label;
+ gchar *path;
+
+ g_return_if_fail (label != NULL);
+ canon_label = gimp_menu_shell_make_canonical_path (label);
+ path = g_strdup_printf ("%s/%s",
+ model->priv->path ? model->priv->path : "",
+ canon_label);
+ g_free (canon_label);
+
+ submodel = gimp_menu_model_new_submenu (model->priv->manager, submenu, path);
+ item = g_menu_item_new_submenu (label, G_MENU_MODEL (submodel));
+
+ g_object_unref (submodel);
+ g_free (path);
+ }
+ else
+ {
+ item = g_menu_item_new_from_model (G_MENU_MODEL (gmodel), i);
+
+ if (action_name)
+ {
+ GAction *action;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (app),
+ action_name + 4);
+ g_signal_connect_object (action,
+ "notify::visible",
+ G_CALLBACK (gimp_menu_model_action_notify_visible),
+ model, 0);
+
+ g_menu_item_set_label (item, gimp_action_get_label (GIMP_ACTION (action)));
+ g_signal_connect_object (action,
+ "notify::label",
+ G_CALLBACK (gimp_menu_model_action_notify_label),
+ item, 0);
+ }
+ }
+
+ if (item)
+ model->priv->items = g_list_append (model->priv->items, item);
+
+ g_free (label);
+ g_free (action_name);
+ g_clear_object (&subsection);
+ g_clear_object (&submenu);
+ }
+
+ if (! model->priv->is_section)
+ {
+ g_signal_connect_object (model->priv->manager, "ui-added",
+ G_CALLBACK (gimp_menu_model_ui_added),
+ model, 0);
+ g_signal_connect_object (model->priv->manager, "ui-removed",
+ G_CALLBACK (gimp_menu_model_ui_removed),
+ model, 0);
+ gimp_ui_manager_foreach_ui (model->priv->manager,
+ (GimpUIMenuCallback) gimp_menu_model_ui_added,
+ model);
+ }
+}
+
+static void
+gimp_menu_model_update (GimpMenuModel *model)
+{
+ gint last_n_items;
+ gint n_items;
+
+ g_return_if_fail (GIMP_IS_MENU_MODEL (model));
+
+ last_n_items = model->priv->last_n_items;
+ n_items = gimp_menu_model_get_n_items (G_MENU_MODEL (model));
+
+ /* A bit "lazy" but should make sure we don't mess up. */
+ g_menu_model_items_changed (G_MENU_MODEL (model), 0, last_n_items, n_items);
+}
+
+static GMenuItem *
+gimp_menu_model_get_item (GimpMenuModel *model,
+ gint idx)
+{
+ GimpMenuModel *m = GIMP_MENU_MODEL (model);
+ GApplication *app = m->priv->manager->gimp->app;
+ gint cur = -1;
+
+ for (GList *iter = m->priv->items; iter; iter = iter->next)
+ {
+ const gchar *action_name = NULL;
+ GMenuModel *subsection;
+ GMenuModel *submenu;
+
+ subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
+ submenu = g_menu_item_get_link (iter->data, G_MENU_LINK_SUBMENU);
+
+ if (subsection || submenu)
+ {
+ cur++;
+ }
+ /* Count neither placeholders (items with no action_name), nor invisible
+ * actions.
+ */
+ else if (g_menu_item_get_attribute (iter->data,
+ G_MENU_ATTRIBUTE_ACTION,
+ "&s", &action_name))
+ {
+ GAction *action;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (app),
+ action_name + 4);
+ if (gimp_action_is_visible (GIMP_ACTION (action)))
+ cur++;
+ }
+
+ g_clear_object (&subsection);
+ g_clear_object (&submenu);
+
+ if (cur == idx)
+ return iter->data;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_menu_model_action_notify_visible (GimpAction *action,
+ GParamSpec *pspec,
+ GimpMenuModel *model)
+{
+ gimp_menu_model_update (model);
+}
+
+static void
+gimp_menu_model_action_notify_label (GimpAction *action,
+ GParamSpec *pspec,
+ GMenuItem *item)
+{
+ g_return_if_fail (GIMP_IS_ACTION (action));
+ g_return_if_fail (G_IS_MENU_ITEM (item));
+
+ g_menu_item_set_label (item, gimp_action_get_label (action));
+}
+
+static gboolean
+gimp_menu_model_ui_added (GimpUIManager *manager,
+ const gchar *path,
+ const gchar *action_name,
+ const gchar *placeholder_key,
+ gboolean top,
+ GimpMenuModel *model)
+{
+ gboolean added = FALSE;
+
+ if (g_strcmp0 (path, model->priv->path) == 0)
+ {
+ GApplication *app = model->priv->manager->gimp->app;
+ GAction *action;
+ gchar *detailed_action_name;
+ GMenuItem *item;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
+
+ g_return_val_if_fail (action != NULL, FALSE);
+
+ added = TRUE;
+
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (gimp_menu_model_action_notify_visible),
+ model);
+ detailed_action_name = g_strdup_printf ("app.%s", g_action_get_name (action));
+ item = g_menu_item_new (gimp_action_get_label (GIMP_ACTION (action)), detailed_action_name);
+ /* TODO: add also G_MENU_ATTRIBUTE_ICON attribute? */
+ g_free (detailed_action_name);
+
+ if (placeholder_key)
+ {
+ GList *placeholder = NULL;
+ GMenuModel *subsection = NULL;
+
+ for (GList *iter = model->priv->items; iter; iter = iter->next)
+ {
+ const gchar *label;
+
+ subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
+
+ if (subsection != NULL)
+ {
+ if (gimp_menu_model_ui_added (manager, path, action_name,
+ placeholder_key, top,
+ GIMP_MENU_MODEL (subsection)))
+ break;
+ else
+ g_clear_object (&subsection);
+ }
+ else if (g_menu_item_get_attribute (iter->data,
+ G_MENU_ATTRIBUTE_LABEL,
+ "&s", &label) &&
+ g_strcmp0 (label, placeholder_key) == 0)
+ {
+ placeholder = iter;
+ break;
+ }
+ }
+
+ if (placeholder)
+ {
+ if (top)
+ model->priv->items = g_list_insert_before (model->priv->items,
+ placeholder, item);
+ else
+ model->priv->items = g_list_insert_before (model->priv->items,
+ placeholder->next, item);
+ }
+ else
+ {
+ added = FALSE;
+
+ if (subsection == NULL && ! model->priv->is_section)
+ g_warning ("%s: no placeholder item '%s'.", G_STRFUNC, placeholder_key);
+ }
+ /* else: added in a subsection. */
+
+ g_clear_object (&subsection);
+ }
+ else if (top)
+ {
+ model->priv->items = g_list_prepend (model->priv->items, item);
+ }
+ else
+ {
+ model->priv->items = g_list_append (model->priv->items, item);
+ }
+
+ g_signal_connect_object (action,
+ "notify::visible",
+ G_CALLBACK (gimp_menu_model_action_notify_visible),
+ model, 0);
+ g_signal_connect_object (action,
+ "notify::label",
+ G_CALLBACK (gimp_menu_model_action_notify_label),
+ item, 0);
+
+ gimp_menu_model_update (model);
+ }
+
+ return added;
+}
+
+static gboolean
+gimp_menu_model_ui_removed (GimpUIManager *manager,
+ const gchar *path,
+ const gchar *action_name,
+ GimpMenuModel *model)
+{
+ gboolean removed = FALSE;
+
+ if (g_strcmp0 (path, model->priv->path) == 0)
+ {
+ GApplication *app = model->priv->manager->gimp->app;
+ GMenuItem *item = NULL;
+ GMenuModel *subsection = NULL;
+ GAction *action;
+
+ action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
+
+ g_return_val_if_fail (action != NULL, FALSE);
+
+ removed = TRUE;
+
+ for (GList *iter = model->priv->items; iter; iter = iter->next)
+ {
+ const gchar *action;
+
+ subsection = g_menu_item_get_link (iter->data, G_MENU_LINK_SECTION);
+
+ if (subsection != NULL)
+ {
+ if (gimp_menu_model_ui_removed (manager, path, action_name,
+ GIMP_MENU_MODEL (subsection)))
+ break;
+ else
+ g_clear_object (&subsection);
+ }
+ else if (g_menu_item_get_attribute (iter->data,
+ G_MENU_ATTRIBUTE_ACTION,
+ "&s", &action) &&
+ /* "action" attribute will start with "app." prefix. */
+ g_strcmp0 (action + 4, action_name) == 0)
+ {
+ item = iter->data;
+ model->priv->items = g_list_delete_link (model->priv->items, iter);
+ break;
+ }
+ }
+
+ if (item)
+ {
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (gimp_menu_model_action_notify_visible),
+ model);
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (gimp_menu_model_action_notify_label),
+ item);
+ g_object_unref (item);
+ }
+ else
+ {
+ removed = FALSE;
+
+ if (subsection == NULL && ! model->priv->is_section)
+ g_warning ("%s: no item for action name '%s'.", G_STRFUNC, action_name);
+ }
+ /* else: removed in a subsection. */
+
+ g_clear_object (&subsection);
+
+ gimp_menu_model_update (model);
+ }
+
+ return removed;
+}
diff --git a/app/widgets/gimpmenumodel.h b/app/widgets/gimpmenumodel.h
new file mode 100644
index 0000000000..11128f1721
--- /dev/null
+++ b/app/widgets/gimpmenumodel.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpmenu_model.h
+ * Copyright (C) 2023 Jehan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __GIMP_MENU_MODEL_H__
+#define __GIMP_MENU_MODEL_H__
+
+
+#define GIMP_TYPE_MENU_MODEL (gimp_menu_model_get_type ())
+#define GIMP_MENU_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MENU_MODEL, GimpMenuModel))
+#define GIMP_MENU_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MENU_MODEL, GimpMenuModelClass))
+#define GIMP_IS_MENU_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_MENU_MODEL))
+#define GIMP_IS_MENU_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MENU_MODEL))
+#define GIMP_MENU_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MENU_MODEL, GimpMenuModelClass))
+
+
+typedef struct _GimpMenuModelPrivate GimpMenuModelPrivate;
+typedef struct _GimpMenuModelClass GimpMenuModelClass;
+
+struct _GimpMenuModel
+{
+ GMenuModel parent_instance;
+
+ GimpMenuModelPrivate *priv;
+};
+
+struct _GimpMenuModelClass
+{
+ GMenuModelClass parent_class;
+};
+
+
+GType gimp_menu_model_get_type (void) G_GNUC_CONST;
+
+GimpMenuModel * gimp_menu_model_new (GimpUIManager *manager,
+ GMenuModel *model);
+
+GimpMenuModel * gimp_menu_model_get_submodel (GimpMenuModel *model,
+ const gchar *path);
+
+const gchar * gimp_menu_model_get_path (GimpMenuModel *model);
+
+
+#endif /* __GIMP_MENU_MODEL_H__ */
diff --git a/app/widgets/gimpmenushell.c b/app/widgets/gimpmenushell.c
index abc9afbdcc..5902646a1b 100644
--- a/app/widgets/gimpmenushell.c
+++ b/app/widgets/gimpmenushell.c
@@ -28,6 +28,7 @@
#include "widgets-types.h"
#include "gimpmenu.h"
+#include "gimpmenumodel.h"
#include "gimpmenushell.h"
#include "gimpuimanager.h"
@@ -39,8 +40,8 @@ typedef struct _GimpMenuShellPrivate GimpMenuShellPrivate;
struct _GimpMenuShellPrivate
{
GimpUIManager *manager;
- gchar *update_signal;
- gchar **path_prefix;
+ GimpMenuModel *model;
+ gchar *path_prefix;
GRegex *path_regex;
};
@@ -50,19 +51,14 @@ static GimpMenuShellPrivate *
gimp_menu_shell_get_private (GimpMenuShell *menu_shell);
static void gimp_menu_shell_private_finalize (GimpMenuShellPrivate *priv);
-static void gimp_menu_shell_ui_added (GimpUIManager *manager,
- const gchar *path,
- const gchar *action_name,
- const gchar *placeholder_key,
- gboolean top,
- GimpMenuShell *shell);
-static void gimp_menu_shell_ui_removed (GimpUIManager *manager,
- const gchar *path,
- const gchar *action_name,
+static void gimp_menu_shell_model_changed (GMenuModel *self,
+ gint position,
+ gint removed,
+ gint added,
GimpMenuShell *shell);
static void gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
- GMenuModel *model);
+ GimpMenuModel *model);
static gchar ** gimp_menu_shell_break_path (GimpMenuShell *shell,
const gchar *path);
@@ -84,65 +80,67 @@ gimp_menu_shell_default_init (GimpMenuShellInterface *iface)
GIMP_TYPE_UI_MANAGER,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
+ g_object_interface_install_property (iface,
+ g_param_spec_object ("model",
+ NULL, NULL,
+ GIMP_TYPE_MENU_MODEL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_READWRITE));
}
-/* public functions */
+/* Public functions. */
void
gimp_menu_shell_fill (GimpMenuShell *shell,
- GMenuModel *model,
- const gchar *update_signal,
+ GimpMenuModel *model,
gboolean drop_top_submenu)
{
- GimpMenuShellPrivate *priv;
- gchar **path_prefix;
-
g_return_if_fail (GTK_IS_CONTAINER (shell));
gtk_container_foreach (GTK_CONTAINER (shell),
(GtkCallback) gtk_widget_destroy,
NULL);
- priv = GET_PRIVATE (shell);
- g_clear_pointer (&priv->path_prefix, g_strfreev);
- path_prefix = g_object_get_data (G_OBJECT (model), "gimp-ui-manager-model-paths");
- priv->path_prefix = g_strdupv (path_prefix);
-
if (drop_top_submenu)
gimp_menu_shell_append_model_drop_top (shell, model);
else
- GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, model);
-
- if (update_signal != NULL)
- {
- if (priv->update_signal != NULL)
- {
- g_free (priv->update_signal);
- g_signal_handlers_disconnect_by_func (priv->manager,
- G_CALLBACK (gimp_menu_shell_ui_added),
- shell);
- g_signal_handlers_disconnect_by_func (priv->manager,
- G_CALLBACK (gimp_menu_shell_ui_removed),
- shell);
- }
-
- priv->update_signal = g_strdup (update_signal);
- g_signal_connect_object (priv->manager, update_signal,
- G_CALLBACK (gimp_menu_shell_ui_added),
- shell, 0);
- g_signal_connect_object (priv->manager, "ui-removed",
- G_CALLBACK (gimp_menu_shell_ui_removed),
- shell, 0);
- gimp_ui_manager_foreach_ui (priv->manager,
- (GimpUIMenuCallback) gimp_menu_shell_ui_added,
- shell);
- }
+ gimp_menu_shell_append (shell, model);
}
/* Protected functions. */
+void
+gimp_menu_shell_append (GimpMenuShell *shell,
+ GimpMenuModel *model)
+{
+ GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
+
+ g_free (priv->path_prefix);
+ priv->path_prefix = g_strdup (gimp_menu_model_get_path (GIMP_MENU_MODEL (model)));
+
+ if (priv->model)
+ g_signal_handlers_disconnect_by_func (priv->model,
+ G_CALLBACK (gimp_menu_shell_model_changed),
+ shell);
+
+ if (priv->model != model)
+ {
+ g_clear_object (&priv->model);
+ priv->model = g_object_ref (model);
+ }
+
+ if (model)
+ {
+ GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, model);
+
+ g_signal_connect_object (priv->model, "items-changed",
+ G_CALLBACK (gimp_menu_shell_model_changed),
+ shell, 0);
+ }
+}
+
void
gimp_menu_shell_init (GimpMenuShell *shell)
{
@@ -152,8 +150,8 @@ gimp_menu_shell_init (GimpMenuShell *shell)
priv = GET_PRIVATE (shell);
- priv->update_signal = NULL;
priv->path_prefix = NULL;
+ priv->model = NULL;
priv->path_regex = g_regex_new ("/+", 0, 0, NULL);
}
@@ -169,6 +167,7 @@ void
gimp_menu_shell_install_properties (GObjectClass *klass)
{
g_object_class_override_property (klass, GIMP_MENU_SHELL_PROP_MANAGER, "manager");
+ g_object_class_override_property (klass, GIMP_MENU_SHELL_PROP_MODEL, "model");
}
void
@@ -186,6 +185,9 @@ gimp_menu_shell_get_property (GObject *object,
case GIMP_MENU_SHELL_PROP_MANAGER:
g_value_set_object (value, priv->manager);
break;
+ case GIMP_MENU_SHELL_PROP_MODEL:
+ g_value_set_object (value, priv->model);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -199,15 +201,18 @@ gimp_menu_shell_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- GimpMenuShellPrivate *priv;
+ GimpMenuShellPrivate *priv = GET_PRIVATE (object);
+ GimpMenuShell *shell = GIMP_MENU_SHELL (object);
- priv = GET_PRIVATE (object);
switch (property_id)
{
case GIMP_MENU_SHELL_PROP_MANAGER:
g_set_weak_pointer (&priv->manager, g_value_get_object (value));
break;
+ case GIMP_MENU_SHELL_PROP_MODEL:
+ gimp_menu_shell_append (shell, g_value_get_object (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -265,84 +270,64 @@ gimp_menu_shell_get_private (GimpMenuShell *menu_shell)
static void
gimp_menu_shell_private_finalize (GimpMenuShellPrivate *priv)
{
- g_free (priv->update_signal);
- g_clear_pointer (&priv->path_prefix, g_strfreev);
+ g_free (priv->path_prefix);
g_clear_pointer (&priv->path_regex, g_regex_unref);
+ g_clear_object (&priv->model);
g_slice_free (GimpMenuShellPrivate, priv);
}
static void
-gimp_menu_shell_ui_added (GimpUIManager *manager,
- const gchar *path,
- const gchar *action_name,
- const gchar *placeholder_key,
- gboolean top,
- GimpMenuShell *shell)
+gimp_menu_shell_model_changed (GMenuModel *model,
+ gint position,
+ gint removed,
+ gint added,
+ GimpMenuShell *shell)
{
- gchar **paths = NULL;
- gint paths_idx = 0;
+ GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
- if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui &&
- gimp_menu_shell_is_subpath (shell, path, &paths, &paths_idx))
- GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui (shell,
- (const gchar **) (paths + paths_idx),
- action_name,
- placeholder_key, top);
-
- g_strfreev (paths);
-}
-
-static void
-gimp_menu_shell_ui_removed (GimpUIManager *manager,
- const gchar *path,
- const gchar *action_name,
- GimpMenuShell *shell)
-{
- gchar **paths = NULL;
- gint paths_idx = 0;
-
- if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->remove_ui &&
- gimp_menu_shell_is_subpath (shell, path, &paths, &paths_idx))
- GIMP_MENU_SHELL_GET_INTERFACE (shell)->remove_ui (shell,
- (const gchar **) paths + paths_idx,
- action_name);
-
- g_strfreev (paths);
+ /* Lazy approach to handle model changes. A better implementation would only
+ * remove or delete the changed items using:
+ * GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui()
+ * GIMP_MENU_SHELL_GET_INTERFACE (shell)->remove_ui
+ * TODO
+ */
+ gtk_container_foreach (GTK_CONTAINER (shell),
+ (GtkCallback) gtk_widget_destroy,
+ NULL);
+ GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, priv->model);
}
static void
gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
- GMenuModel *model)
+ GimpMenuModel *model)
{
- GMenuModel *submenu = NULL;
+ GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
+ GMenuModel *submenu = NULL;
g_return_if_fail (GTK_IS_CONTAINER (shell));
- if (g_menu_model_get_n_items (model) == 1)
+ if (g_menu_model_get_n_items (G_MENU_MODEL (model)) == 1)
{
- GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
- gchar *label = NULL;
+ gchar *label = NULL;
- submenu = g_menu_model_get_item_link (model, 0, G_MENU_LINK_SUBMENU);
+ submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), 0, G_MENU_LINK_SUBMENU);
if (submenu)
{
- GStrvBuilder *paths_builder = g_strv_builder_new ();
+ gchar *path_prefix;
g_menu_model_get_item_attribute (submenu, 0, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- g_strv_builder_addv (paths_builder, (const char **) priv->path_prefix);
- g_strv_builder_add (paths_builder, label);
- g_strfreev (priv->path_prefix);
- priv->path_prefix = g_strv_builder_end (paths_builder);
- g_strv_builder_unref (paths_builder);
+ path_prefix = g_strdup_printf ("%s/%s", priv->path_prefix, label);
+ g_free (priv->path_prefix);
+ priv->path_prefix = path_prefix;
}
g_free (label);
}
- GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, submenu != NULL ? submenu : model);
+ gimp_menu_shell_append (shell, submenu != NULL ? submenu : model);
g_clear_object (&submenu);
}
@@ -390,7 +375,7 @@ gimp_menu_shell_is_subpath (GimpMenuShell *shell,
gint *index)
{
GimpMenuShellPrivate *priv;
- gboolean is_subpath = TRUE;
+ gboolean is_subpath = TRUE;
*index = 0;
*paths = gimp_menu_shell_break_path (shell, path);
@@ -398,18 +383,23 @@ gimp_menu_shell_is_subpath (GimpMenuShell *shell,
if (priv->path_prefix)
{
- *index = g_strv_length (priv->path_prefix);
+ gchar **path_prefix;
+
+ path_prefix = gimp_menu_shell_break_path (shell, priv->path_prefix);
+ *index = g_strv_length (path_prefix);
is_subpath = FALSE;
if (*index <= g_strv_length (*paths))
{
gint i;
for (i = 0; i < *index; i ++)
- if (g_strcmp0 (priv->path_prefix[i], (*paths)[i]) != 0)
+ if (g_strcmp0 (path_prefix[i], (*paths)[i]) != 0)
break;
is_subpath = (i == *index);
}
+
+ g_strfreev (path_prefix);
}
return is_subpath;
diff --git a/app/widgets/gimpmenushell.h b/app/widgets/gimpmenushell.h
index 8a19170044..0267e83dce 100644
--- a/app/widgets/gimpmenushell.h
+++ b/app/widgets/gimpmenushell.h
@@ -32,8 +32,9 @@ enum
{
GIMP_MENU_SHELL_PROP_0,
GIMP_MENU_SHELL_PROP_MANAGER,
+ GIMP_MENU_SHELL_PROP_MODEL,
- GIMP_MENU_SHELL_PROP_LAST = GIMP_MENU_SHELL_PROP_MANAGER,
+ GIMP_MENU_SHELL_PROP_LAST = GIMP_MENU_SHELL_PROP_MODEL,
};
typedef struct _GimpMenuShellInterface GimpMenuShellInterface;
@@ -44,8 +45,8 @@ struct _GimpMenuShellInterface
/* Virtual functions. */
- void (* append) (GimpMenuShell *shell,
- GMenuModel *model);
+ void (* append) (GimpMenuShell *shell,
+ GimpMenuModel *model);
void (* add_ui) (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name,
@@ -60,13 +61,15 @@ struct _GimpMenuShellInterface
GType gimp_menu_shell_get_type (void) G_GNUC_CONST;
void gimp_menu_shell_fill (GimpMenuShell *shell,
- GMenuModel *model,
- const gchar *update_signal,
+ GimpMenuModel *model,
gboolean drop_top_submenu);
/* Protected functions. */
+void gimp_menu_shell_append (GimpMenuShell *shell,
+ GimpMenuModel *model);
+
void gimp_menu_shell_init (GimpMenuShell *shell);
void gimp_menu_shell_install_properties (GObjectClass *klass);
void gimp_menu_shell_get_property (GObject *object,
diff --git a/app/widgets/gimptexteditor.c b/app/widgets/gimptexteditor.c
index dbcb2898ae..c27959eced 100644
--- a/app/widgets/gimptexteditor.c
+++ b/app/widgets/gimptexteditor.c
@@ -139,7 +139,7 @@ gimp_text_editor_new (const gchar *title,
GimpTextEditor *editor;
GtkWidget *content_area;
GtkWidget *toolbar;
- GMenuModel *toolbar_model;
+ GimpMenuModel *toolbar_model;
GtkWidget *style_editor;
GtkWidget *scrolled_window;
gboolean use_header_bar;
diff --git a/app/widgets/gimptoolbar.c b/app/widgets/gimptoolbar.c
index f210ab4826..f45bb60652 100644
--- a/app/widgets/gimptoolbar.c
+++ b/app/widgets/gimptoolbar.c
@@ -35,6 +35,7 @@
#include "gimpradioaction.h"
#include "gimptoggleaction.h"
#include "gimptoolbar.h"
+#include "gimpmenumodel.h"
#include "gimpmenushell.h"
#include "gimpuimanager.h"
@@ -53,36 +54,13 @@
* actions as buttons).
*/
-enum
-{
- PROP_0 = GIMP_MENU_SHELL_PROP_LAST,
- PROP_MODEL
-};
-
-
-struct _GimpToolbarPrivate
-{
- GMenuModel *model;
-};
-
/* local function prototypes */
static void gimp_toolbar_iface_init (GimpMenuShellInterface *iface);
-static void gimp_toolbar_constructed (GObject *object);
-static void gimp_toolbar_dispose (GObject *object);
-static void gimp_toolbar_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gimp_toolbar_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec);
-
static void gimp_toolbar_append (GimpMenuShell *shell,
- GMenuModel *model);
+ GimpMenuModel *model);
static void gimp_toolbar_add_ui (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name,
@@ -108,7 +86,6 @@ static void gimp_toolbar_action_notify_visible (GimpAction *act
G_DEFINE_TYPE_WITH_CODE (GimpToolbar, gimp_toolbar, GTK_TYPE_TOOLBAR,
- G_ADD_PRIVATE (GimpToolbar)
G_IMPLEMENT_INTERFACE (GIMP_TYPE_MENU_SHELL, gimp_toolbar_iface_init))
#define parent_class gimp_toolbar_parent_class
@@ -121,19 +98,10 @@ gimp_toolbar_class_init (GimpToolbarClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->constructed = gimp_toolbar_constructed;
- object_class->dispose = gimp_toolbar_dispose;
- object_class->get_property = gimp_toolbar_get_property;
- object_class->set_property = gimp_toolbar_set_property;
+ object_class->get_property = gimp_menu_shell_get_property;
+ object_class->set_property = gimp_menu_shell_set_property;
gimp_menu_shell_install_properties (object_class);
-
- g_object_class_install_property (object_class, PROP_MODEL,
- g_param_spec_object ("model",
- NULL, NULL,
- G_TYPE_MENU_MODEL,
- GIMP_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
}
static void
@@ -146,78 +114,19 @@ gimp_toolbar_iface_init (GimpMenuShellInterface *iface)
static void
gimp_toolbar_init (GimpToolbar *bar)
{
- bar->priv = gimp_toolbar_get_instance_private (bar);
-
gimp_menu_shell_init (GIMP_MENU_SHELL (bar));
}
-static void
-gimp_toolbar_constructed (GObject *object)
-{
- G_OBJECT_CLASS (parent_class)->constructed (object);
-}
-
-static void
-gimp_toolbar_dispose (GObject *object)
-{
- GimpToolbar *bar = GIMP_TOOLBAR (object);
-
- g_clear_object (&bar->priv->model);
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gimp_toolbar_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GimpToolbar *bar = GIMP_TOOLBAR (object);
-
- switch (property_id)
- {
- case PROP_MODEL:
- bar->priv->model = g_value_dup_object (value);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (bar), bar->priv->model, "ui-added", FALSE);
- break;
-
- default:
- gimp_menu_shell_set_property (object, property_id, value, pspec);
- break;
- }
-}
-
-static void
-gimp_toolbar_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GimpToolbar *bar = GIMP_TOOLBAR (object);
-
- switch (property_id)
- {
- case PROP_MODEL:
- g_value_set_object (value, bar->priv->model);
- break;
-
- default:
- gimp_menu_shell_get_property (object, property_id, value, pspec);
- break;
- }
-}
-
static void
gimp_toolbar_append (GimpMenuShell *shell,
- GMenuModel *model)
+ GimpMenuModel *model)
{
GimpToolbar *toolbar = GIMP_TOOLBAR (shell);
gint n_items;
g_return_if_fail (GTK_IS_CONTAINER (shell));
- n_items = g_menu_model_get_n_items (model);
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (model));
for (gint i = 0; i < n_items; i++)
{
GMenuModel *subsection;
@@ -225,10 +134,10 @@ gimp_toolbar_append (GimpMenuShell *shell,
gchar *label = NULL;
gchar *action_name = NULL;
- subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
- submenu = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
+ 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);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_menu_model_get_item_attribute (G_MENU_MODEL (model), i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
if (subsection != NULL)
{
@@ -238,7 +147,7 @@ gimp_toolbar_append (GimpMenuShell *shell,
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
gtk_widget_show (GTK_WIDGET (item));
- gimp_toolbar_append (shell, subsection);
+ gimp_toolbar_append (shell, GIMP_MENU_MODEL (subsection));
item = gtk_separator_tool_item_new ();
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
@@ -290,7 +199,7 @@ gimp_toolbar_add_ui (GimpMenuShell *shell,
/* Public functions */
GtkWidget *
-gimp_toolbar_new (GMenuModel *model,
+gimp_toolbar_new (GimpMenuModel *model,
GimpUIManager *manager)
{
g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager) &&
diff --git a/app/widgets/gimptoolbar.h b/app/widgets/gimptoolbar.h
index e44f75a225..64b1c51485 100644
--- a/app/widgets/gimptoolbar.h
+++ b/app/widgets/gimptoolbar.h
@@ -48,7 +48,7 @@ struct _GimpToolbarClass
GType gimp_toolbar_get_type (void) G_GNUC_CONST;
-GtkWidget * gimp_toolbar_new (GMenuModel *model,
+GtkWidget * gimp_toolbar_new (GimpMenuModel *model,
GimpUIManager *manager);
diff --git a/app/widgets/gimpuimanager.c b/app/widgets/gimpuimanager.c
index 3c9fa3f9bf..769ddd73c2 100644
--- a/app/widgets/gimpuimanager.c
+++ b/app/widgets/gimpuimanager.c
@@ -38,6 +38,7 @@
#include "gimphelp.h"
#include "gimphelp-ids.h"
#include "gimpmenu.h"
+#include "gimpmenumodel.h"
#include "gimpmenushell.h"
#include "gimptoggleaction.h"
#include "gimpuimanager.h"
@@ -108,8 +109,6 @@ static GtkWidget *find_widget_under_pointer (GdkWindow *window,
gint *x,
gint *y);
-static void gimp_ui_manager_fill_model (GimpUIManager *manager,
- GMenuModel *model);
static void gimp_ui_manager_menu_item_free (GimpUIManagerMenuItem *item);
static void gimp_ui_manager_popup_hidden (GtkMenuShell *popup,
@@ -478,16 +477,16 @@ gimp_ui_manager_get_widget (GimpUIManager *manager,
return gtk_ui_manager_get_widget ((GtkUIManager *) manager, path);
}
-GMenuModel *
+GimpMenuModel *
gimp_ui_manager_get_model (GimpUIManager *manager,
const gchar *path)
{
GimpUIManagerUIEntry *entry;
- GMenuModel *model;
+ GimpMenuModel *model;
+ GMenuModel *gmodel;
+ GimpMenuModel *submodel;
gchar *root;
gchar *submenus;
- GStrvBuilder *paths_builder = g_strv_builder_new ();
- GStrv paths;
root = g_strdup (path);
submenus = strstr (root + 1, "/");
@@ -545,66 +544,21 @@ gimp_ui_manager_get_model (GimpUIManager *manager,
/* The model is owned by the builder which I have to keep around. */
entry->builder = gtk_builder_new_from_file (filename);
- gimp_ui_manager_fill_model (manager,
- G_MENU_MODEL (gtk_builder_get_object (entry->builder, root)));
-
g_free (filename);
g_free (full_basename);
}
- model = G_MENU_MODEL (gtk_builder_get_object (entry->builder, root));
+ gmodel = G_MENU_MODEL (gtk_builder_get_object (entry->builder, root));
- g_return_val_if_fail (G_IS_MENU (model), NULL);
+ g_return_val_if_fail (G_IS_MENU (gmodel), NULL);
- g_object_ref (model);
- while (submenus != NULL)
- {
- const gchar *submenu = submenus;
- gint n_items;
- gint i;
+ model = gimp_menu_model_new (manager, gmodel);
- submenus = strstr (submenu + 1, "/");
- if (submenus != NULL)
- {
- *submenus = '\0';
- if (*(++submenus) == '\0')
- submenus = NULL;
- }
+ submodel = gimp_menu_model_get_submodel (model, submenus);
- n_items = g_menu_model_get_n_items (model);
- for (i = 0; i < n_items; i++)
- {
- GMenuModel *submodel;
- gchar *label = NULL;
- gchar *canon_label = NULL;
-
- submodel = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- if (label)
- canon_label = gimp_menu_shell_make_canonical_path (label);
-
- if (submodel && g_strcmp0 (canon_label, submenu) == 0)
- {
- g_strv_builder_add (paths_builder, canon_label);
- g_object_unref (model);
- model = submodel;
- g_free (label);
- break;
- }
- g_clear_object (&submodel);
- g_free (label);
- g_free (canon_label);
- }
- g_return_val_if_fail (i < n_items, NULL);
- }
-
- paths = g_strv_builder_end (paths_builder);
- g_strv_builder_unref (paths_builder);
- g_object_set_data_full (G_OBJECT (model), "gimp-ui-manager-model-paths",
- paths, (GDestroyNotify) g_strfreev);
g_free (root);
- return model;
+ return submodel;
}
void
@@ -801,19 +755,19 @@ gimp_ui_manager_ui_popup_at_widget (GimpUIManager *manager,
GDestroyNotify popdown_func,
gpointer popdown_data)
{
- GMenuModel *model;
- GtkWidget *menu;
+ GimpMenuModel *model;
+ GtkWidget *menu;
g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
g_return_if_fail (ui_path != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
- menu = gimp_menu_new (manager);
+ menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL);
model = gimp_ui_manager_get_model (manager, ui_path);
g_return_if_fail (model != NULL);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
+ gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, TRUE);
g_object_unref (model);
if (! menu)
@@ -821,13 +775,13 @@ gimp_ui_manager_ui_popup_at_widget (GimpUIManager *manager,
if (child_ui_manager != NULL && child_ui_path != NULL)
{
- GMenuModel *child_model;
- GtkWidget *child_menu;
+ GimpMenuModel *child_model;
+ GtkWidget *child_menu;
/* TODO GMenu: the "icon" attribute set in the .ui file should be visible. */
child_model = gimp_ui_manager_get_model (child_ui_manager, child_ui_path);
child_menu = gimp_menu_new (child_ui_manager);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (child_menu), child_model, "ui-added", FALSE);
+ gimp_menu_shell_fill (GIMP_MENU_SHELL (child_menu), child_model, FALSE);
g_object_unref (child_model);
gimp_menu_merge (GIMP_MENU (menu), GIMP_MENU (child_menu), TRUE);
@@ -859,8 +813,8 @@ gimp_ui_manager_ui_popup_at_pointer (GimpUIManager *manager,
GDestroyNotify popdown_func,
gpointer popdown_data)
{
- GMenuModel *model;
- GtkWidget *menu;
+ GimpMenuModel *model;
+ GtkWidget *menu;
g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
g_return_if_fail (ui_path != NULL);
@@ -868,7 +822,7 @@ gimp_ui_manager_ui_popup_at_pointer (GimpUIManager *manager,
model = gimp_ui_manager_get_model (manager, ui_path);
menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
+ gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, TRUE);
g_object_unref (model);
if (! menu)
@@ -901,8 +855,8 @@ gimp_ui_manager_ui_popup_at_rect (GimpUIManager *manager,
GDestroyNotify popdown_func,
gpointer popdown_data)
{
- GMenuModel *model;
- GtkWidget *menu;
+ GimpMenuModel *model;
+ GtkWidget *menu;
g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
g_return_if_fail (ui_path != NULL);
@@ -910,7 +864,7 @@ gimp_ui_manager_ui_popup_at_rect (GimpUIManager *manager,
model = gimp_ui_manager_get_model (manager, ui_path);
menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
- gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
+ gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, TRUE);
g_object_unref (model);
if (! menu)
@@ -1401,55 +1355,6 @@ find_widget_under_pointer (GdkWindow *window,
return event_widget;
}
-static void
-gimp_ui_manager_fill_model (GimpUIManager *manager,
- GMenuModel *model)
-{
- gint n_items;
-
- g_return_if_fail (G_IS_MENU_MODEL (model));
-
- n_items = g_menu_model_get_n_items (model);
- for (int i = 0; i < n_items; i++)
- {
- GMenuModel *subsection;
- GMenuModel *submenu;
- gchar *label = NULL;
- gchar *action_name = NULL;
-
- subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
- submenu = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
- g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
-
- if (subsection != NULL)
- {
- gimp_ui_manager_fill_model (manager, subsection);
- }
- else if (submenu != NULL)
- {
- gimp_ui_manager_fill_model (manager, submenu);
- }
- else if (action_name != NULL)
- {
- /* Update the label, unless it's a placeholder (no action name). */
- GApplication *app;
- GAction *action;
-
- app = manager->gimp->app;
- action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name + 4);
- g_menu_remove (G_MENU (model), i);
- g_menu_insert (G_MENU (model), i,
- gimp_action_get_label (GIMP_ACTION (action)),
- action_name);
- }
- g_free (label);
- g_free (action_name);
- g_clear_object (&subsection);
- g_clear_object (&submenu);
- }
-}
-
static void
gimp_ui_manager_menu_item_free (GimpUIManagerMenuItem *item)
{
diff --git a/app/widgets/gimpuimanager.h b/app/widgets/gimpuimanager.h
index 8d7463dc04..ebeb4cf839 100644
--- a/app/widgets/gimpuimanager.h
+++ b/app/widgets/gimpuimanager.h
@@ -110,7 +110,7 @@ GtkAccelGroup * gimp_ui_manager_get_accel_group (GimpUIManager *manager);
GtkWidget * gimp_ui_manager_get_widget (GimpUIManager *manager,
const gchar *path);
-GMenuModel * gimp_ui_manager_get_model (GimpUIManager *manager,
+GimpMenuModel * gimp_ui_manager_get_model (GimpUIManager *manager,
const gchar *path);
void gimp_ui_manager_remove_ui (GimpUIManager *manager,
diff --git a/app/widgets/meson.build b/app/widgets/meson.build
index df27613023..baeecd4c91 100644
--- a/app/widgets/meson.build
+++ b/app/widgets/meson.build
@@ -142,6 +142,7 @@ libappwidgets_sources = [
'gimpmenushell.c',
'gimpmenudock.c',
'gimpmenufactory.c',
+ 'gimpmenumodel.c',
'gimpmessagebox.c',
'gimpmessagedialog.c',
'gimpmeter.c',
diff --git a/app/widgets/widgets-types.h b/app/widgets/widgets-types.h
index 8ba3fff160..d69119ff7a 100644
--- a/app/widgets/widgets-types.h
+++ b/app/widgets/widgets-types.h
@@ -212,6 +212,7 @@ typedef struct _GimpLayerModeComboBox GimpLayerModeComboBox;
typedef struct _GimpMessageBox GimpMessageBox;
typedef struct _GimpMenu GimpMenu;
typedef struct _GimpMenuBar GimpMenuBar;
+typedef struct _GimpMenuModel GimpMenuModel;
typedef struct _GimpMenuShell GimpMenuShell;
typedef struct _GimpMeter GimpMeter;
typedef struct _GimpModifiersEditor GimpModifiersEditor;