app: properly remove action from the application when no longer used.

When an action is removed from a group, we verify if it's not in any group
anymore. If so, we remove it from the GimpApp action map.

This fixes some CRITICALs happening when some actions were dynamically deleted
and re-created, in particular the "windows-display-XXXX" actions.
This commit is contained in:
Jehan 2023-03-28 22:30:38 +02:00
parent 50a3bb57ee
commit 6bed6e1ef8
4 changed files with 67 additions and 18 deletions

View file

@ -31,11 +31,16 @@
#include "core/gimp.h" #include "core/gimp.h"
#include "gimpaction.h"
#include "gimpactionfactory.h" #include "gimpactionfactory.h"
#include "gimpactiongroup.h" #include "gimpactiongroup.h"
static void gimp_action_factory_finalize (GObject *object); static void gimp_action_factory_finalize (GObject *object);
static void gimp_action_factory_action_removed (GimpActionGroup *group,
GimpAction *action,
GimpActionFactory *factory);
G_DEFINE_TYPE (GimpActionFactory, gimp_action_factory, GIMP_TYPE_OBJECT) G_DEFINE_TYPE (GimpActionFactory, gimp_action_factory, GIMP_TYPE_OBJECT)
@ -82,6 +87,40 @@ gimp_action_factory_finalize (GObject *object)
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
static void
gimp_action_factory_action_removed (GimpActionGroup *group,
GimpAction *action,
GimpActionFactory *factory)
{
GList *list;
for (list = factory->registered_groups; list; list = g_list_next (list))
{
GimpActionFactoryEntry *entry = list->data;
if (entry->group != NULL && entry->group != group)
{
GList *actions;
actions = gimp_action_group_list_actions (entry->group);
if (g_list_find (actions, action))
{
g_list_free (actions);
break;
}
g_list_free (actions);
}
}
if (list == NULL)
g_action_map_remove_action (G_ACTION_MAP (factory->gimp->app),
gimp_action_get_name (action));
}
/* Public functions */
GimpActionFactory * GimpActionFactory *
gimp_action_factory_new (Gimp *gimp) gimp_action_factory_new (Gimp *gimp)
{ {
@ -155,6 +194,10 @@ gimp_action_factory_get_group (GimpActionFactory *factory,
entry->setup_func (group); entry->setup_func (group);
entry->group = group; entry->group = group;
g_signal_connect_object (group, "action-removed",
G_CALLBACK (gimp_action_factory_action_removed),
factory, 0);
} }
return entry->group; return entry->group;

View file

@ -49,6 +49,7 @@
enum enum
{ {
ACTION_ADDED, ACTION_ADDED,
ACTION_REMOVED,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -123,6 +124,14 @@ gimp_action_group_class_init (GimpActionGroupClass *klass)
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
GIMP_TYPE_ACTION); GIMP_TYPE_ACTION);
signals[ACTION_REMOVED] =
g_signal_new ("action-removed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GimpActionGroupClass, action_removed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
GIMP_TYPE_ACTION);
} }
static void static void
@ -347,13 +356,7 @@ gimp_action_group_remove_action (GimpActionGroup *group,
{ {
group->actions = g_list_remove (group->actions, action); group->actions = g_list_remove (group->actions, action);
/* TODO GAction: we should also check if the action is still present in g_signal_emit (group, signals[ACTION_REMOVED], 0, action);
* another group (maybe if each action keeps track of its own groups, or with
* gimp_ui_manager_find_action(), or a "action-removed" signal tracked by the
* GimpUIManager which would verify other groups).
* If it's not in any group anymore, we should remove the action with
* g_action_map_remove_action().
*/
} }
GimpAction * GimpAction *

View file

@ -55,8 +55,10 @@ struct _GimpActionGroupClass
GHashTable *groups; GHashTable *groups;
/* signals */ /* signals */
void (* action_added) (GimpActionGroup *group, void (* action_added) (GimpActionGroup *group,
GimpAction *action); GimpAction *action);
void (* action_removed) (GimpActionGroup *group,
GimpAction *action);
}; };

View file

@ -906,8 +906,6 @@ gimp_menu_model_ui_removed (GimpUIManager *manager,
action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name); action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
g_return_val_if_fail (action != NULL, FALSE);
removed = TRUE; removed = TRUE;
for (GList *iter = model->priv->items; iter; iter = iter->next) for (GList *iter = model->priv->items; iter; iter = iter->next)
@ -938,12 +936,15 @@ gimp_menu_model_ui_removed (GimpUIManager *manager,
if (item) if (item)
{ {
g_signal_handlers_disconnect_by_func (action, if (action != NULL)
G_CALLBACK (gimp_menu_model_action_notify_visible), {
model); g_signal_handlers_disconnect_by_func (action,
g_signal_handlers_disconnect_by_func (action, G_CALLBACK (gimp_menu_model_action_notify_visible),
G_CALLBACK (gimp_menu_model_action_notify_label), model);
item); g_signal_handlers_disconnect_by_func (action,
G_CALLBACK (gimp_menu_model_action_notify_label),
item);
}
g_object_unref (item); g_object_unref (item);
} }
else else