mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 17:33:25 +00:00

Even if the container is empty, then we return a GStrv of length 1 (i.e.
an array of length 1, terminating with NULL).
In particular, it improves gimp_container_get_filtered_name_array() as
well, and in turn various list-returning functions in libgimp. This
makes the API more consistent after changes from commit 8eb7f6df9e
.
Note that a NULL return can be acceptable for error cases, but an empty
list because this is the expected result for the request should be an
empty GStrv, not NULL.
1161 lines
34 KiB
C
1161 lines
34 KiB
C
/* 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 "libgimpbase/gimpbase.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, NULL,
|
|
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, NULL,
|
|
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, NULL,
|
|
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, NULL,
|
|
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_IFACE (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_IFACE (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
|
|
*
|
|
* Returns: (nullable) (transfer none): 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
|
|
*
|
|
* Returns: (nullable) (transfer none): 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)
|
|
{
|
|
gchar **names;
|
|
gchar **iter;
|
|
gint length;
|
|
|
|
g_return_val_if_fail (GIMP_IS_CONTAINER (container), NULL);
|
|
|
|
length = gimp_container_get_n_children (container);
|
|
|
|
names = iter = g_new0 (gchar *, length + 1);
|
|
|
|
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;
|
|
}
|
|
}
|