/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis * * gimpcontainer.c * Copyright (C) 2001 Michael Natterer * * 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 "libgimpconfig/gimpconfig.h" #include "core-types.h" #include "gimp.h" #include "gimp-memsize.h" #include "gimpcontainer.h" #include "gimpmarshal.h" /* #define DEBUG_CONTAINER */ #ifdef DEBUG_CONTAINER #define D(stmnt) stmnt #else #define D(stmnt) #endif enum { ADD, REMOVE, REORDER, FREEZE, THAW, LAST_SIGNAL }; enum { PROP_0, PROP_CHILDREN_TYPE, PROP_POLICY }; typedef struct { gchar *signame; GCallback callback; gpointer callback_data; GQuark quark; /* used to attach the signal id's of child signals */ } GimpContainerHandler; struct _GimpContainerPrivate { GType children_type; GimpContainerPolicy policy; gint n_children; GList *handlers; gint freeze_count; }; /* local function prototypes */ static void gimp_container_config_iface_init (GimpConfigInterface *iface); static void gimp_container_dispose (GObject *object); static void gimp_container_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_container_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gint64 gimp_container_get_memsize (GimpObject *object, gint64 *gui_size); static void gimp_container_real_add (GimpContainer *container, GimpObject *object); static void gimp_container_real_remove (GimpContainer *container, GimpObject *object); static gboolean gimp_container_serialize (GimpConfig *config, GimpConfigWriter *writer, gpointer data); static gboolean gimp_container_deserialize (GimpConfig *config, GScanner *scanner, gint nest_level, gpointer data); static void gimp_container_disconnect_callback (GimpObject *object, gpointer data); static void gimp_container_free_handler (GimpContainer *container, GimpContainerHandler *handler); G_DEFINE_TYPE_WITH_CODE (GimpContainer, gimp_container, GIMP_TYPE_OBJECT, G_ADD_PRIVATE (GimpContainer) G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, gimp_container_config_iface_init)) #define parent_class gimp_container_parent_class static guint container_signals[LAST_SIGNAL] = { 0, }; static void gimp_container_class_init (GimpContainerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); container_signals[ADD] = g_signal_new ("add", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpContainerClass, add), NULL, NULL, gimp_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GIMP_TYPE_OBJECT); container_signals[REMOVE] = g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpContainerClass, remove), NULL, NULL, gimp_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GIMP_TYPE_OBJECT); container_signals[REORDER] = g_signal_new ("reorder", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpContainerClass, reorder), NULL, NULL, gimp_marshal_VOID__OBJECT_INT, G_TYPE_NONE, 2, GIMP_TYPE_OBJECT, G_TYPE_INT); container_signals[FREEZE] = g_signal_new ("freeze", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GimpContainerClass, freeze), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); container_signals[THAW] = g_signal_new ("thaw", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GimpContainerClass, thaw), NULL, NULL, gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); object_class->dispose = gimp_container_dispose; object_class->set_property = gimp_container_set_property; object_class->get_property = gimp_container_get_property; gimp_object_class->get_memsize = gimp_container_get_memsize; klass->add = gimp_container_real_add; klass->remove = gimp_container_real_remove; klass->reorder = NULL; klass->freeze = NULL; klass->thaw = NULL; klass->clear = NULL; klass->have = NULL; klass->foreach = NULL; klass->search = NULL; klass->get_unique_names = NULL; klass->get_child_by_name = NULL; klass->get_child_by_index = NULL; klass->get_child_index = NULL; g_object_class_install_property (object_class, PROP_CHILDREN_TYPE, g_param_spec_gtype ("children-type", NULL, NULL, GIMP_TYPE_OBJECT, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_POLICY, g_param_spec_enum ("policy", NULL, NULL, GIMP_TYPE_CONTAINER_POLICY, GIMP_CONTAINER_POLICY_STRONG, GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void gimp_container_config_iface_init (GimpConfigInterface *iface) { iface->serialize = gimp_container_serialize; iface->deserialize = gimp_container_deserialize; } static void gimp_container_init (GimpContainer *container) { container->priv = gimp_container_get_instance_private (container); container->priv->handlers = NULL; container->priv->freeze_count = 0; container->priv->children_type = G_TYPE_NONE; container->priv->policy = GIMP_CONTAINER_POLICY_STRONG; container->priv->n_children = 0; } static void gimp_container_dispose (GObject *object) { GimpContainer *container = GIMP_CONTAINER (object); gimp_container_clear (container); while (container->priv->handlers) gimp_container_remove_handler (container, ((GimpContainerHandler *) container->priv->handlers->data)->quark); if (container->priv->children_type != G_TYPE_NONE) { g_type_class_unref (g_type_class_peek (container->priv->children_type)); container->priv->children_type = G_TYPE_NONE; } G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_container_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpContainer *container = GIMP_CONTAINER (object); switch (property_id) { case PROP_CHILDREN_TYPE: container->priv->children_type = g_value_get_gtype (value); g_type_class_ref (container->priv->children_type); break; case PROP_POLICY: container->priv->policy = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_container_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpContainer *container = GIMP_CONTAINER (object); switch (property_id) { case PROP_CHILDREN_TYPE: g_value_set_gtype (value, container->priv->children_type); break; case PROP_POLICY: g_value_set_enum (value, container->priv->policy); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gint64 gimp_container_get_memsize (GimpObject *object, gint64 *gui_size) { GimpContainer *container = GIMP_CONTAINER (object); gint64 memsize = 0; GList *list; for (list = container->priv->handlers; list; list = g_list_next (list)) { GimpContainerHandler *handler = list->data; memsize += (sizeof (GList) + sizeof (GimpContainerHandler) + gimp_string_get_memsize (handler->signame)); } return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); } static void gimp_container_real_add (GimpContainer *container, GimpObject *object) { container->priv->n_children++; } static void gimp_container_real_remove (GimpContainer *container, GimpObject *object) { container->priv->n_children--; } typedef struct { GimpConfigWriter *writer; gpointer data; gboolean success; } SerializeData; static void gimp_container_serialize_foreach (GObject *object, SerializeData *serialize_data) { GimpConfigInterface *config_iface; const gchar *name; config_iface = GIMP_CONFIG_GET_INTERFACE (object); if (! config_iface) serialize_data->success = FALSE; if (! serialize_data->success) return; gimp_config_writer_open (serialize_data->writer, g_type_name (G_TYPE_FROM_INSTANCE (object))); name = gimp_object_get_name (object); if (name) gimp_config_writer_string (serialize_data->writer, name); else gimp_config_writer_print (serialize_data->writer, "NULL", 4); serialize_data->success = config_iface->serialize (GIMP_CONFIG (object), serialize_data->writer, serialize_data->data); gimp_config_writer_close (serialize_data->writer); } static gboolean gimp_container_serialize (GimpConfig *config, GimpConfigWriter *writer, gpointer data) { GimpContainer *container = GIMP_CONTAINER (config); SerializeData serialize_data; serialize_data.writer = writer; serialize_data.data = data; serialize_data.success = TRUE; gimp_container_foreach (container, (GFunc) gimp_container_serialize_foreach, &serialize_data); return serialize_data.success; } static gboolean gimp_container_deserialize (GimpConfig *config, GScanner *scanner, gint nest_level, gpointer data) { GimpContainer *container = GIMP_CONTAINER (config); GTokenType token; token = G_TOKEN_LEFT_PAREN; while (g_scanner_peek_next_token (scanner) == token) { token = g_scanner_get_next_token (scanner); switch (token) { case G_TOKEN_LEFT_PAREN: token = G_TOKEN_IDENTIFIER; break; case G_TOKEN_IDENTIFIER: { GimpObject *child = NULL; GType type; gchar *name = NULL; gboolean add_child = FALSE; type = g_type_from_name (scanner->value.v_identifier); if (! type) { g_scanner_error (scanner, "unable to determine type of '%s'", scanner->value.v_identifier); return FALSE; } if (! g_type_is_a (type, container->priv->children_type)) { g_scanner_error (scanner, "'%s' is not a subclass of '%s'", scanner->value.v_identifier, g_type_name (container->priv->children_type)); return FALSE; } if (! g_type_is_a (type, GIMP_TYPE_CONFIG)) { g_scanner_error (scanner, "'%s' does not implement GimpConfigInterface", scanner->value.v_identifier); return FALSE; } if (! gimp_scanner_parse_string (scanner, &name)) { token = G_TOKEN_STRING; break; } if (! name) name = g_strdup (""); if (gimp_container_get_unique_names (container)) child = gimp_container_get_child_by_name (container, name); if (! child) { if (GIMP_IS_GIMP (data)) child = g_object_new (type, "gimp", data, NULL); else child = g_object_new (type, NULL); add_child = TRUE; } /* always use the deserialized name. while it normally * doesn't make a difference there are obscure case like * template migration. */ gimp_object_take_name (child, name); if (! GIMP_CONFIG_GET_INTERFACE (child)->deserialize (GIMP_CONFIG (child), scanner, nest_level + 1, NULL)) { if (add_child) g_object_unref (child); /* warning should be already set by child */ return FALSE; } if (add_child) { gimp_container_add (container, child); if (container->priv->policy == GIMP_CONTAINER_POLICY_STRONG) g_object_unref (child); } } token = G_TOKEN_RIGHT_PAREN; break; case G_TOKEN_RIGHT_PAREN: token = G_TOKEN_LEFT_PAREN; break; default: /* do nothing */ break; } } return gimp_config_deserialize_return (scanner, token, nest_level); } static void gimp_container_disconnect_callback (GimpObject *object, gpointer data) { GimpContainer *container = GIMP_CONTAINER (data); gimp_container_remove (container, object); } static void gimp_container_free_handler_foreach_func (GimpObject *object, GimpContainerHandler *handler) { gulong handler_id; handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object), handler->quark)); if (handler_id) { g_signal_handler_disconnect (object, handler_id); g_object_set_qdata (G_OBJECT (object), handler->quark, NULL); } } static void gimp_container_free_handler (GimpContainer *container, GimpContainerHandler *handler) { D (g_print ("%s: id = %d\n", G_STRFUNC, handler->quark)); gimp_container_foreach (container, (GFunc) gimp_container_free_handler_foreach_func, handler); g_free (handler->signame); g_slice_free (GimpContainerHandler, handler); } GType gimp_container_get_children_type (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), G_TYPE_NONE); return container->priv->children_type; } GimpContainerPolicy gimp_container_get_policy (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); return container->priv->policy; } gint gimp_container_get_n_children (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); return container->priv->n_children; } gboolean gimp_container_add (GimpContainer *container, GimpObject *object) { GList *list; gint n_children; g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); g_return_val_if_fail (object != NULL, FALSE); g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, container->priv->children_type), FALSE); if (gimp_container_have (container, object)) { g_warning ("%s: container %p already contains object %p", G_STRFUNC, container, object); return FALSE; } for (list = container->priv->handlers; list; list = g_list_next (list)) { GimpContainerHandler *handler = list->data; gulong handler_id; handler_id = g_signal_connect (object, handler->signame, handler->callback, handler->callback_data); g_object_set_qdata (G_OBJECT (object), handler->quark, GUINT_TO_POINTER (handler_id)); } switch (container->priv->policy) { case GIMP_CONTAINER_POLICY_STRONG: g_object_ref (object); break; case GIMP_CONTAINER_POLICY_WEAK: g_signal_connect (object, "disconnect", G_CALLBACK (gimp_container_disconnect_callback), container); break; } n_children = container->priv->n_children; g_signal_emit (container, container_signals[ADD], 0, object); if (n_children == container->priv->n_children) { g_warning ("%s: GimpContainer::add() implementation did not " "chain up. Please report this at https://www.gimp.org/bugs/", G_STRFUNC); container->priv->n_children++; } return TRUE; } gboolean gimp_container_remove (GimpContainer *container, GimpObject *object) { GList *list; gint n_children; g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); g_return_val_if_fail (object != NULL, FALSE); g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, container->priv->children_type), FALSE); if (! gimp_container_have (container, object)) { g_warning ("%s: container %p does not contain object %p", G_STRFUNC, container, object); return FALSE; } for (list = container->priv->handlers; list; list = g_list_next (list)) { GimpContainerHandler *handler = list->data; gulong handler_id; handler_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (object), handler->quark)); if (handler_id) { g_signal_handler_disconnect (object, handler_id); g_object_set_qdata (G_OBJECT (object), handler->quark, NULL); } } n_children = container->priv->n_children; g_signal_emit (container, container_signals[REMOVE], 0, object); if (n_children == container->priv->n_children) { g_warning ("%s: GimpContainer::remove() implementation did not " "chain up. Please report this at https://www.gimp.org/bugs/", G_STRFUNC); container->priv->n_children--; } switch (container->priv->policy) { case GIMP_CONTAINER_POLICY_STRONG: g_object_unref (object); break; case GIMP_CONTAINER_POLICY_WEAK: g_signal_handlers_disconnect_by_func (object, gimp_container_disconnect_callback, container); break; } return TRUE; } gboolean gimp_container_insert (GimpContainer *container, GimpObject *object, gint index) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); g_return_val_if_fail (object != NULL, FALSE); g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, container->priv->children_type), FALSE); g_return_val_if_fail (index >= -1 && index <= container->priv->n_children, FALSE); if (gimp_container_have (container, object)) { g_warning ("%s: container %p already contains object %p", G_STRFUNC, container, object); return FALSE; } if (gimp_container_add (container, object)) { return gimp_container_reorder (container, object, index); } return FALSE; } gboolean gimp_container_reorder (GimpContainer *container, GimpObject *object, gint new_index) { gint index; g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); g_return_val_if_fail (object != NULL, FALSE); g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, container->priv->children_type), FALSE); g_return_val_if_fail (new_index >= -1 && new_index < container->priv->n_children, FALSE); if (new_index == -1) new_index = container->priv->n_children - 1; index = gimp_container_get_child_index (container, object); if (index == -1) { g_warning ("%s: container %p does not contain object %p", G_STRFUNC, container, object); return FALSE; } if (index != new_index) g_signal_emit (container, container_signals[REORDER], 0, object, new_index); return TRUE; } void gimp_container_freeze (GimpContainer *container) { g_return_if_fail (GIMP_IS_CONTAINER (container)); container->priv->freeze_count++; if (container->priv->freeze_count == 1) g_signal_emit (container, container_signals[FREEZE], 0); } void gimp_container_thaw (GimpContainer *container) { g_return_if_fail (GIMP_IS_CONTAINER (container)); if (container->priv->freeze_count > 0) container->priv->freeze_count--; if (container->priv->freeze_count == 0) g_signal_emit (container, container_signals[THAW], 0); } gboolean gimp_container_frozen (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); return (container->priv->freeze_count > 0) ? TRUE : FALSE; } gint gimp_container_freeze_count (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); return container->priv->freeze_count; } void gimp_container_clear (GimpContainer *container) { g_return_if_fail (GIMP_IS_CONTAINER (container)); if (container->priv->n_children > 0) { gimp_container_freeze (container); GIMP_CONTAINER_GET_CLASS (container)->clear (container); gimp_container_thaw (container); } } gboolean gimp_container_is_empty (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); return (container->priv->n_children == 0); } gboolean gimp_container_have (GimpContainer *container, GimpObject *object) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); if (container->priv->n_children < 1) return FALSE; return GIMP_CONTAINER_GET_CLASS (container)->have (container, object); } void gimp_container_foreach (GimpContainer *container, GFunc func, gpointer user_data) { g_return_if_fail (GIMP_IS_CONTAINER (container)); g_return_if_fail (func != NULL); if (container->priv->n_children > 0) GIMP_CONTAINER_GET_CLASS (container)->foreach (container, func, user_data); } GimpObject * gimp_container_search (GimpContainer *container, GimpContainerSearchFunc func, gpointer user_data) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); g_return_val_if_fail (func != NULL, NULL); if (container->priv->n_children > 0) { return GIMP_CONTAINER_GET_CLASS (container)->search (container, func, user_data); } return NULL; } gboolean gimp_container_get_unique_names (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), FALSE); if (GIMP_CONTAINER_GET_CLASS (container)->get_unique_names) return GIMP_CONTAINER_GET_CLASS (container)->get_unique_names (container); return FALSE; } GimpObject * gimp_container_get_child_by_name (GimpContainer *container, const gchar *name) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); if (!name) return NULL; return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_name (container, name); } GimpObject * gimp_container_get_child_by_index (GimpContainer *container, gint index) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); if (index < 0 || index >= container->priv->n_children) return NULL; return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, index); } /** * gimp_container_get_first_child: * @container: a #GimpContainer * * Return value: the first child object stored in @container or %NULL if the * container is empty */ GimpObject * gimp_container_get_first_child (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); if (container->priv->n_children > 0) return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, 0); return NULL; } /** * gimp_container_get_last_child: * @container: a #GimpContainer * * Return value: the last child object stored in @container or %NULL if the * container is empty */ GimpObject * gimp_container_get_last_child (GimpContainer *container) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); if (container->priv->n_children > 0) return GIMP_CONTAINER_GET_CLASS (container)->get_child_by_index (container, container->priv->n_children - 1); return NULL; } gint gimp_container_get_child_index (GimpContainer *container, GimpObject *object) { g_return_val_if_fail (GIMP_IS_CONTAINER (container), -1); g_return_val_if_fail (object != NULL, -1); g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (object, container->priv->children_type), -1); return GIMP_CONTAINER_GET_CLASS (container)->get_child_index (container, object); } GimpObject * gimp_container_get_neighbor_of (GimpContainer *container, GimpObject *object) { gint index; g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); g_return_val_if_fail (GIMP_IS_OBJECT (object), NULL); index = gimp_container_get_child_index (container, object); if (index != -1) { GimpObject *new; new = gimp_container_get_child_by_index (container, index + 1); if (! new && index > 0) new = gimp_container_get_child_by_index (container, index - 1); return new; } return NULL; } static void gimp_container_get_name_array_foreach_func (GimpObject *object, gchar ***iter) { gchar **array = *iter; *array = g_strdup (gimp_object_get_name (object)); (*iter)++; } gchar ** gimp_container_get_name_array (GimpContainer *container, gint *length) { gchar **names; gchar **iter; g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL); g_return_val_if_fail (length != NULL, NULL); *length = gimp_container_get_n_children (container); if (*length == 0) return NULL; names = iter = g_new (gchar *, *length); gimp_container_foreach (container, (GFunc) gimp_container_get_name_array_foreach_func, &iter); return names; } static void gimp_container_add_handler_foreach_func (GimpObject *object, GimpContainerHandler *handler) { gulong handler_id; handler_id = g_signal_connect (object, handler->signame, handler->callback, handler->callback_data); g_object_set_qdata (G_OBJECT (object), handler->quark, GUINT_TO_POINTER (handler_id)); } GQuark gimp_container_add_handler (GimpContainer *container, const gchar *signame, GCallback callback, gpointer callback_data) { GimpContainerHandler *handler; gchar *key; static gint handler_id = 0; g_return_val_if_fail (GIMP_IS_CONTAINER (container), 0); g_return_val_if_fail (signame != NULL, 0); g_return_val_if_fail (callback != NULL, 0); if (! g_str_has_prefix (signame, "notify::")) g_return_val_if_fail (g_signal_lookup (signame, container->priv->children_type), 0); handler = g_slice_new0 (GimpContainerHandler); /* create a unique key for this handler */ key = g_strdup_printf ("%s-%d", signame, handler_id++); handler->signame = g_strdup (signame); handler->callback = callback; handler->callback_data = callback_data; handler->quark = g_quark_from_string (key); D (g_print ("%s: key = %s, id = %d\n", G_STRFUNC, key, handler->quark)); g_free (key); container->priv->handlers = g_list_prepend (container->priv->handlers, handler); gimp_container_foreach (container, (GFunc) gimp_container_add_handler_foreach_func, handler); return handler->quark; } void gimp_container_remove_handler (GimpContainer *container, GQuark id) { GimpContainerHandler *handler; GList *list; g_return_if_fail (GIMP_IS_CONTAINER (container)); g_return_if_fail (id != 0); for (list = container->priv->handlers; list; list = g_list_next (list)) { handler = (GimpContainerHandler *) list->data; if (handler->quark == id) break; } if (! list) { g_warning ("%s: tried to remove handler which unknown id %d", G_STRFUNC, id); return; } gimp_container_free_handler (container, handler); container->priv->handlers = g_list_delete_link (container->priv->handlers, list); } void gimp_container_remove_handlers_by_func (GimpContainer *container, GCallback callback, gpointer callback_data) { GList *list; g_return_if_fail (GIMP_IS_CONTAINER (container)); g_return_if_fail (callback != NULL); list = container->priv->handlers; while (list) { GimpContainerHandler *handler = list->data; GList *next = g_list_next (list); if (handler->callback == callback && handler->callback_data == callback_data) { gimp_container_free_handler (container, handler); container->priv->handlers = g_list_delete_link ( container->priv->handlers, list); } list = next; } } void gimp_container_remove_handlers_by_data (GimpContainer *container, gpointer callback_data) { GList *list; g_return_if_fail (GIMP_IS_CONTAINER (container)); list = container->priv->handlers; while (list) { GimpContainerHandler *handler = list->data; GList *next = g_list_next (list); if (handler->callback_data == callback_data) { gimp_container_free_handler (container, handler); container->priv->handlers = g_list_delete_link ( container->priv->handlers, list); } list = next; } }