diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
commit | e42129241681dde7adae7d20697e7b421682fbb4 (patch) | |
tree | af1fe815a5e639e68e59fabd8395ec69458b3e5e /app/core/gimpcontainer.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream/2.10.22.tar.xz gimp-upstream/2.10.22.zip |
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | app/core/gimpcontainer.c | 1167 |
1 files changed, 1167 insertions, 0 deletions
diff --git a/app/core/gimpcontainer.c b/app/core/gimpcontainer.c new file mode 100644 index 0000000..322a3ec --- /dev/null +++ b/app/core/gimpcontainer.c @@ -0,0 +1,1167 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpcontainer.c + * Copyright (C) 2001 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 <gio/gio.h> +#include <gegl.h> + +#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; + } +} |