diff --git a/ChangeLog b/ChangeLog index d3d645312..8aeeacae5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2008-05-28 Emmanuele Bassi + + Bug 882 - Allow child properties for containers implementing the + ClutterContainer interface (Øyvind Kolås) + + * clutter/clutter-child-meta.[ch]: Base class for the metadata + of a ClutterActor inside a ClutterContainer; the ChildMeta + object implements a wrapper for storing data that is attached + to a ClutterActor only when it's part of a ClutterContainer. + The ChildMeta object is used to store the child properties + accessible through the ClutterContainer API. + + * clutter/clutter-container.[ch]: Creates the ChildMeta for + each actor, in case the Container specifies the ChildMeta + type to use. + + * clutter/Makefile.am: Add clutter-child-meta.[ch] to the build. + + * clutter/clutter-marshal.list: Add the marshaller for the + ClutterContainer::child-notify signal. + + * clutter/clutter-types.h: Declare ClutterContainer and + ClutterChildMeta to avoid recursive inclusion. + +2008-05-28 Neil Roberts + + * clutter/cogl/gles/Makefile.am: Use old-style Makefile rules for + the stringify script so that automake won't complain. + 2008-05-28 Emmanuele Bassi * tests/test-script.c: @@ -24,11 +53,6 @@ table holding the objects - we are already holding it inside the value. -2008-05-28 Neil Roberts - - * clutter/cogl/gles/Makefile.am: Use old-style Makefile rules for - the stringify script so that automake won't complain. - 2008-05-28 Neil Roberts * clutter/cogl/gles/cogl-gles2-wrapper.c (cogl_wrap_glGetFixedv): diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 2f2183832..3f1f3a79a 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -58,6 +58,7 @@ source_h = \ $(srcdir)/clutter-behaviour-path.h \ $(srcdir)/clutter-behaviour-rotate.h \ $(srcdir)/clutter-behaviour-scale.h \ + $(srcdir)/clutter-child-meta.h \ $(srcdir)/clutter-clone-texture.h \ $(srcdir)/clutter-color.h \ $(srcdir)/clutter-container.h \ @@ -143,6 +144,7 @@ source_c = \ clutter-behaviour-path.c \ clutter-behaviour-rotate.c \ clutter-behaviour-scale.c \ + clutter-child-meta.c \ clutter-clone-texture.c \ clutter-color.c \ clutter-container.c \ diff --git a/clutter/clutter-child-meta.c b/clutter/clutter-child-meta.c new file mode 100644 index 000000000..77058359b --- /dev/null +++ b/clutter/clutter-child-meta.c @@ -0,0 +1,160 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * Jorn Baayen + * Emmanuele Bassi + * Tomas Frydrych + * Øyvind Kolås + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:clutter-child-data + * @short_description: Wrapper for actors inside a container + * + * #ClutterChildMeta is a wrapper object created by #ClutterContainer + * implementations in order to store child-specific data and properties. + * + * A #ClutterChildMeta wraps a #ClutterActor inside a #ClutterContainer. + * + * #ClutterChildMeta is available since Clutter 0.8 + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-child-meta.h" +#include "clutter-container.h" +#include "clutter-debug.h" +#include "clutter-private.h" + +G_DEFINE_TYPE (ClutterChildMeta, clutter_child_meta, G_TYPE_OBJECT); + +enum +{ + PROP_0, + + PROP_CONTAINER, + PROP_ACTOR +}; + +static void +clutter_child_meta_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterChildMeta *child_meta = CLUTTER_CHILD_META (object); + + switch (prop_id) + { + case PROP_CONTAINER: + g_value_set_object (value, child_meta->container); + break; + + case PROP_ACTOR: + g_value_set_object (value, child_meta->actor); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_child_meta_class_init (ClutterChildMetaClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->get_property = clutter_child_meta_get_property; + + /** + * ClutterChildMeta:container: + * + * The #ClutterContainer that created this #ClutterChildMeta. + * + * Since: 0.8 + */ + pspec = g_param_spec_object ("container", + "Container", + "The container that created this data", + CLUTTER_TYPE_CONTAINER, + CLUTTER_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_CONTAINER, pspec); + + /** + * ClutterChildMeta:actor: + * + * The #ClutterActor being wrapped by this #ClutterChildMeta + * + * Since: 0.8 + */ + pspec = g_param_spec_object ("actor", + "Actor", + "The actor wrapped by this data", + CLUTTER_TYPE_ACTOR, + CLUTTER_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_ACTOR, pspec); +} + +static void +clutter_child_meta_init (ClutterChildMeta *self) +{ +} + +/** + * clutter_child_meta_get_container: + * @data: a #ClutterChildMeta + * + * Retrieves the container using @data + * + * Return value: a #ClutterContainer + * + * Since: 0.8 + */ +ClutterContainer * +clutter_child_meta_get_container (ClutterChildMeta *data) +{ + g_return_val_if_fail (CLUTTER_IS_CHILD_META (data), NULL); + + return data->container; +} + +/** + * clutter_child_meta_get_actor: + * @data: a #ClutterChildMeta + * + * Retrieves the actor wrapped by @data + * + * Return value: a #ClutterActor + * + * Since: 0.8 + */ +ClutterActor * +clutter_child_meta_get_actor (ClutterChildMeta *data) +{ + g_return_val_if_fail (CLUTTER_IS_CHILD_META (data), NULL); + + return data->actor; +} diff --git a/clutter/clutter-child-meta.h b/clutter/clutter-child-meta.h new file mode 100644 index 000000000..881d66cba --- /dev/null +++ b/clutter/clutter-child-meta.h @@ -0,0 +1,110 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * Jorn Baayen + * Emmanuele Bassi + * Tomas Frydrych + * Øyvind Kolås + * + * Copyright (C) 2008 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_CHILD_META_H__ +#define __CLUTTER_CHILD_META_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_CHILD_META (clutter_child_meta_get_type ()) +#define CLUTTER_CHILD_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CHILD_META, ClutterChildMeta)) +#define CLUTTER_CHILD_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CHILD_META, ClutterChildMetaClass)) +#define CLUTTER_IS_CHILD_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CHILD_META)) +#define CLUTTER_IS_CHILD_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CHILD_META)) +#define CLUTTER_CHILD_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CHILD_META, ClutterChildMetaClass)) + +typedef struct _ClutterChildMetaClass ClutterChildMetaClass; + +/** + * ClutterChildMeta: + * @container: the container handling this data + * @actor: the actor wrapped by this data + * + * Base interface for container specific state for child actors. A child + * data is meant to be used when you need to keep track of information + * about each individual child added to a container. + * + * In order to use it you should create your own subclass of + * #ClutterChildMeta and set the #ClutterContainerIface::child_meta_type + * interface member to your subclass type, like: + * + * |[ + * static void + * my_container_iface_init (ClutterContainerIface *iface) + * { + * /* set the rest of the #ClutterContainer vtable */ + * + * container_iface->child_meta_type = MY_TYPE_CHILD_META; + * } + * ]| + * + * This will automatically create a #ClutterChildMeta of type + * MY_TYPE_CHILD_META for every actor that is added to the container. + * + * The child data for an actor can be retrieved using the + * clutter_container_get_child_meta() function. + * + * The properties of the data and your subclass can be manipulated with + * clutter_container_child_set() and clutter_container_child_get() which + * act like g_object_set() and g_object_get(). + * + * You can provide hooks for your own storage as well as control the + * instantiation by overriding #ClutterContainerIface::create_child_meta, + * #ClutterContainerIface::destroy_child_meta and + * #ClutterContainerIface::get_child_meta. + * + * Since: 0.8 + */ +struct _ClutterChildMeta +{ + /*< private >*/ + GObject parent_instance; + + /*< public >*/ + ClutterContainer *container; + ClutterActor *actor; +}; + +struct _ClutterChildMetaClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +GType clutter_child_meta_get_type (void) G_GNUC_CONST; + +ClutterContainer *clutter_child_meta_get_container (ClutterChildMeta *data); +ClutterActor *clutter_child_meta_get_actor (ClutterChildMeta *data); + +G_END_DECLS + +#endif /* __CLUTTER_CHILD_META_H__ */ diff --git a/clutter/clutter-container.c b/clutter/clutter-container.c index 50c37e95f..aa6c24026 100644 --- a/clutter/clutter-container.c +++ b/clutter/clutter-container.c @@ -32,8 +32,10 @@ #include #include +#include #include "clutter-container.h" +#include "clutter-child-meta.h" #include "clutter-debug.h" #include "clutter-main.h" @@ -41,6 +43,7 @@ #include "clutter-private.h" #include "clutter-enum-types.h" + /** * SECTION:clutter-container * @short_description: An interface for implementing container actors @@ -58,11 +61,33 @@ enum { ACTOR_ADDED, ACTOR_REMOVED, + CHILD_NOTIFY, LAST_SIGNAL }; static guint container_signals[LAST_SIGNAL] = { 0, }; +static GQuark quark_child_meta = 0; + +static ClutterChildMeta * +get_child_meta (ClutterContainer *container, + ClutterActor *actor); + +static void +create_child_meta (ClutterContainer *container, + ClutterActor *actor); + +static void +destroy_child_meta (ClutterContainer *container, + ClutterActor *actor); + +static void +clutter_container_create_child_meta (ClutterContainer *container, + ClutterActor *actor); +static void +clutter_container_destroy_child_meta (ClutterContainer *container, + ClutterActor *actor); + static void clutter_container_base_init (gpointer g_iface) @@ -72,9 +97,13 @@ clutter_container_base_init (gpointer g_iface) if (!initialised) { GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); + ClutterContainerIface *iface = g_iface; initialised = TRUE; + quark_child_meta = + g_quark_from_static_string ("clutter-container-child-data"); + /** * ClutterContainer::actor-added: * @container: the actor which received the signal @@ -86,7 +115,7 @@ clutter_container_base_init (gpointer g_iface) * Since: 0.4 */ container_signals[ACTOR_ADDED] = - g_signal_new ("actor-added", + g_signal_new (I_("actor-added"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContainerIface, actor_added), @@ -105,7 +134,7 @@ clutter_container_base_init (gpointer g_iface) * Since: 0.4 */ container_signals[ACTOR_REMOVED] = - g_signal_new ("actor-removed", + g_signal_new (I_("actor-removed"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContainerIface, actor_removed), @@ -113,6 +142,32 @@ clutter_container_base_init (gpointer g_iface) clutter_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); + + /** + * ClutterContainer::child-notify: + * @container: the container which received the signal + * @actor: the child that has had a property set. + * + * The ::child-notify signal is emitted each time a property is + * being set through the clutter_container_child_set() and + * clutter_container_child_set_property() calls. + * + * Since: 0.8 + */ + container_signals[CHILD_NOTIFY] = + g_signal_new (I_("child-notify"), + iface_type, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ClutterContainerIface, child_notify), + NULL, NULL, + clutter_marshal_VOID__OBJECT_OBJECT_PARAM, + G_TYPE_NONE, 2, + CLUTTER_TYPE_ACTOR, G_TYPE_PARAM); + + iface->child_meta_type = G_TYPE_INVALID; + iface->create_child_meta = create_child_meta; + iface->destroy_child_meta = destroy_child_meta; + iface->get_child_meta = get_child_meta; } } @@ -127,11 +182,11 @@ clutter_container_get_type (void) { sizeof (ClutterContainerIface), clutter_container_base_init, - NULL, /* iface_finalize */ + NULL, /* iface_base_finalize */ }; container_type = g_type_register_static (G_TYPE_INTERFACE, - "ClutterContainer", + I_("ClutterContainer"), &container_info, 0); g_type_interface_add_prerequisite (container_type, G_TYPE_OBJECT); @@ -201,6 +256,7 @@ clutter_container_add_actor (ClutterContainer *container, return; } + clutter_container_create_child_meta (container, actor); CLUTTER_CONTAINER_GET_IFACE (container)->add (container, actor); } @@ -293,6 +349,7 @@ clutter_container_remove_actor (ClutterContainer *container, return; } + clutter_container_destroy_child_meta (container, actor); CLUTTER_CONTAINER_GET_IFACE (container)->remove (container, actor); } @@ -320,7 +377,6 @@ clutter_container_remove_valist (ClutterContainer *container, while (actor) { clutter_container_remove_actor (container, actor); - actor = va_arg (var_args, ClutterActor*); } } @@ -376,7 +432,9 @@ clutter_container_foreach (ClutterContainer *container, g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (callback != NULL); - CLUTTER_CONTAINER_GET_IFACE (container)->foreach (container, callback, user_data); + CLUTTER_CONTAINER_GET_IFACE (container)->foreach (container, + callback, + user_data); } /** @@ -392,7 +450,7 @@ clutter_container_foreach (ClutterContainer *container, void clutter_container_raise_child (ClutterContainer *container, ClutterActor *actor, - ClutterActor *sibling) + ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); @@ -436,7 +494,7 @@ clutter_container_raise_child (ClutterContainer *container, void clutter_container_lower_child (ClutterContainer *container, ClutterActor *actor, - ClutterActor *sibling) + ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); @@ -506,10 +564,11 @@ clutter_container_find_child_by_name (ClutterContainer *container, ClutterActor *actor = NULL; g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); + g_return_val_if_fail (child_name != NULL, NULL); children = clutter_container_get_children (container); - for (iter=children; iter; iter = g_list_next (iter)) + for (iter = children; iter; iter = g_list_next (iter)) { ClutterActor *a; const gchar *iter_name; @@ -532,6 +591,529 @@ clutter_container_find_child_by_name (ClutterContainer *container, break; } } + g_list_free (children); + return actor; } + +static ClutterChildMeta * +get_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + + if (iface->child_meta_type == G_TYPE_INVALID) + return NULL; + else + { + ClutterChildMeta *child_meta = NULL; + GSList *list, *iter; + + list = g_object_get_qdata (G_OBJECT (container), quark_child_meta); + for (iter = list; iter; iter = g_slist_next (iter)) + { + child_meta = iter->data; + + if (child_meta->actor == actor) + return child_meta; + } + } + + return NULL; +} + +static void +create_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + ClutterChildMeta *child_meta = NULL; + GSList *data_list = NULL; + + if (iface->child_meta_type == G_TYPE_INVALID) + return; + + if (!g_type_is_a (iface->child_meta_type, CLUTTER_TYPE_CHILD_META)) + { + g_warning ("%s: Child data of type `%s' is not a ClutterChildMeta", + G_STRLOC, g_type_name (iface->child_meta_type)); + return; + } + + child_meta = g_object_new (iface->child_meta_type, NULL); + child_meta->container = container; + child_meta->actor = actor; + + data_list = g_object_get_qdata (G_OBJECT (container), quark_child_meta); + data_list = g_slist_prepend (data_list, child_meta); + g_object_set_qdata (G_OBJECT (container), quark_child_meta, data_list); +} + +static void +destroy_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + GObject *object = G_OBJECT (container); + + if (iface->child_meta_type == G_TYPE_INVALID) + return; + else + { + ClutterChildMeta *child_meta = NULL; + GSList *list = g_object_get_qdata (object, quark_child_meta); + GSList *iter; + + for (iter = list; iter; iter = g_slist_next (iter)) + { + child_meta = iter->data; + + if (child_meta->actor == actor) + break; + else + child_meta = NULL; + } + + if (child_meta) + { + list = g_slist_remove (list, child_meta); + g_object_set_qdata (object, quark_child_meta, list); + + g_object_unref (child_meta); + } + } +} + +/** + * clutter_container_get_child_meta: + * @container: a #ClutterContainer + * @actor: a #ClutterActor that is a child of @container. + * + * Retrieves the #ClutterChildMeta which contains the data about the + * @container specific state for @actor. + * + * Return value: the #ClutterChildMeta for the @actor child of @container + * or %NULL if the specifiec actor does not exist or the container is not + * configured to provide #ClutterChildMetas + * + * Since: 0.8 + */ +ClutterChildMeta * +clutter_container_get_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + + if (iface->child_meta_type == G_TYPE_INVALID) + return NULL; + + if (G_LIKELY (iface->get_child_meta)) + return iface->get_child_meta (container, actor); + + return NULL; +} + +/* + * clutter_container_create_child_meta: + * @container: a #ClutterContainer + * @actor: a #ClutterActor + * + * Creates the #ClutterChildMeta wrapping @actor inside the + * @container, if the #ClutterContainerIface::child_meta_type + * class member is not set to %G_TYPE_INVALID. + */ +static void +clutter_container_create_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + + if (iface->child_meta_type == G_TYPE_INVALID) + return; + + g_assert (g_type_is_a (iface->child_meta_type, CLUTTER_TYPE_CHILD_META)); + + if (G_LIKELY (iface->create_child_meta)) + iface->create_child_meta (container, actor); +} + +/* + * clutter_container_destroy_child_meta: + * @container: a #ClutterContainer + * @actor: a #ClutterActor + * + * Destroys the #ClutterChildMeta wrapping @actor inside the + * @container, if any. + */ +static void +clutter_container_destroy_child_meta (ClutterContainer *container, + ClutterActor *actor) +{ + ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); + + if (iface->child_meta_type == G_TYPE_INVALID) + return; + + if (G_LIKELY (iface->destroy_child_meta)) + iface->destroy_child_meta (container, actor); +} + +/** + * clutter_container_class_find_child_property: + * @klass: a #GObjectClass implementing the #ClutterContainer interface. + * @property_name: a property name. + * + * Looks up the #GParamSpec for a child property of @klass. + * + * Return value: The #GParamSpec for the property or %NULL if no such + * property exist. + * + * Since: 0.8 + */ +GParamSpec * +clutter_container_class_find_child_property (GObjectClass *klass, + const gchar *property_name) +{ + ClutterContainerIface *iface; + GObjectClass *child_class; + GParamSpec *pspec; + + g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); + g_return_val_if_fail (property_name != NULL, NULL); + g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_CLASS (klass), + CLUTTER_TYPE_CONTAINER), + NULL); + + iface = g_type_interface_peek (klass, CLUTTER_TYPE_CONTAINER); + g_return_val_if_fail (iface != NULL, NULL); + + if (iface->child_meta_type == G_TYPE_INVALID) + return NULL; + + child_class = g_type_class_ref (iface->child_meta_type); + pspec = g_object_class_find_property (child_class, property_name); + g_type_class_unref (child_class); + + return pspec; +} + +/** + * clutter_container_class_list_child_properties: + * @klass: a #GObjectClass implementing the #ClutterContainer interface. + * @n_properties: return location for length of returned array. + * + * Returns an array of #GParamSpec for all child properties. + * + * Return value: an array of #GParamSpecs which should be freed + * after use. + * + * Since: 0.8 + */ +GParamSpec ** +clutter_container_class_list_child_properties (GObjectClass *klass, + guint *n_properties) +{ + ClutterContainerIface *iface; + GObjectClass *child_class; + GParamSpec **retval; + + g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); + g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_CLASS (klass), + CLUTTER_TYPE_CONTAINER), + NULL); + + iface = g_type_interface_peek (klass, CLUTTER_TYPE_CONTAINER); + g_return_val_if_fail (iface != NULL, NULL); + + if (iface->child_meta_type == G_TYPE_INVALID) + return NULL; + + child_class = g_type_class_ref (iface->child_meta_type); + retval = g_object_class_list_properties (child_class, n_properties); + g_type_class_unref (child_class); + + return retval; +} + +static inline void +container_set_child_property (ClutterContainer *container, + ClutterActor *actor, + const GValue *value, + GParamSpec *pspec) +{ + ClutterChildMeta *data; + ClutterContainerIface *iface; + + data = clutter_container_get_child_meta (container, actor); + g_object_set_property (G_OBJECT (data), pspec->name, value); + + iface = CLUTTER_CONTAINER_GET_IFACE (container); + if (G_LIKELY (iface->child_notify)) + iface->child_notify (container, actor, pspec); +} + +/** + * clutter_container_child_set_property: + * @container: a #ClutterContainer + * @actor: a #ClutterActor that is a child of @container. + * @property: the name of the property to set. + * @value: the value. + * + * Sets a container-specific property on a child of @container. + * + * Since: 0.8 + */ +void +clutter_container_child_set_property (ClutterContainer *container, + ClutterActor *actor, + const gchar *property, + const GValue *value) +{ + GObjectClass *klass; + GParamSpec *pspec; + + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (property != NULL); + g_return_if_fail (value != NULL); + + klass = G_OBJECT_GET_CLASS (container); + + pspec = clutter_container_class_find_child_property (klass, property); + if (!pspec) + { + g_warning ("%s: Containers of type `%s' have no child " + "property named `%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (container), property); + return; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: Child property `%s' of the container `%s' " + "is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); + return; + } + + container_set_child_property (container, actor, value, pspec); +} + +/** + * clutter_container_child_set: + * @container: a #ClutterContainer + * @actor: a #ClutterActor that is a child of @container. + * @first_prop: name of the first property to be set. + * @...: value for the first property, followed optionally by more name/value + * pairs terminated with NULL. + * + * Sets container specific properties on the child of a container. + * + * Since: 0.8 + */ +void +clutter_container_child_set (ClutterContainer *container, + ClutterActor *actor, + const gchar *first_prop, + ...) +{ + GObjectClass *klass; + const gchar *name; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + klass = G_OBJECT_GET_CLASS (container); + + va_start (var_args, first_prop); + + name = first_prop; + while (name) + { + GValue value = { 0, }; + gchar *error = NULL; + GParamSpec *pspec; + + pspec = clutter_container_class_find_child_property (klass, name); + if (!pspec) + { + g_warning ("%s: Containers of type `%s' have no child " + "property named `%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (container), name); + break; + } + + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: Child property `%s' of the container `%s' " + "is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + G_VALUE_COLLECT (&value, var_args, 0, &error); + if (error) + { + /* we intentionally leak the GValue because it might + * be in an undefined state and calling g_value_unset() + * on it might crash + */ + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + break; + } + + container_set_child_property (container, actor, &value, pspec); + + g_value_unset (&value); + + name = va_arg (var_args, gchar*); + } + + va_end (var_args); +} + +static inline void +container_get_child_property (ClutterContainer *container, + ClutterActor *actor, + GValue *value, + GParamSpec *pspec) +{ + ClutterChildMeta *data; + + data = clutter_container_get_child_meta (container, actor); + g_object_set_property (G_OBJECT (data), pspec->name, value); +} + +/** + * clutter_container_child_get_property: + * @container: a #ClutterContainer + * @actor: a #ClutterActor that is a child of @container. + * @property: the name of the property to set. + * @value: the value. + * + * Gets a container specific property of a child of @container, In general, + * a copy is made of the property contents and the caller is responsible for + * freeing the memory by calling g_value_unset(). + * + * Note that clutter_container_child_set_property() is really intended for + * language bindings, clutter_container_child_set() is much more convenient + * for C programming. + * + * Since: 0.8 + */ +void +clutter_container_child_get_property (ClutterContainer *container, + ClutterActor *actor, + const gchar *property, + GValue *value) +{ + GObjectClass *klass; + GParamSpec *pspec; + + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + g_return_if_fail (property != NULL); + g_return_if_fail (value != NULL); + + klass = G_OBJECT_GET_CLASS (container); + + pspec = clutter_container_class_find_child_property (klass, property); + if (!pspec) + { + g_warning ("%s: Containers of type `%s' have no child " + "property named `%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (container), property); + return; + } + + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("%s: Child property `%s' of the container `%s' " + "is not writable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); + return; + } + + container_get_child_property (container, actor, value, pspec); +} + + +/** + * clutter_container_child_get: + * @container: a #ClutterContainer + * @actor: a #ClutterActor that is a child of @container. + * @first_prop: name of the first property to be set. + * @...: value for the first property, followed optionally by more name/value + * pairs terminated with NULL. + * + * Gets @container specific properties of an actor. + * + * In general, a copy is made of the property contents and the caller is + * responsible for freeing the memory in the appropriate manner for the type, for + * instance by calling g_free() or g_object_unref(). + * + * Since: 0.8 + */ +void +clutter_container_child_get (ClutterContainer *container, + ClutterActor *actor, + const gchar *first_prop, + ...) +{ + GObjectClass *klass; + const gchar *name; + va_list var_args; + + g_return_if_fail (CLUTTER_IS_CONTAINER (container)); + g_return_if_fail (CLUTTER_IS_ACTOR (actor)); + + klass = G_OBJECT_GET_CLASS (container); + + va_start (var_args, first_prop); + + name = first_prop; + while (name) + { + GValue value = { 0, }; + gchar *error = NULL; + GParamSpec *pspec; + + pspec = clutter_container_class_find_child_property (klass, name); + if (!pspec) + { + g_warning ("%s: container `%s' has no child property named `%s'", + G_STRLOC, G_OBJECT_TYPE_NAME (container), name); + break; + } + + if (!(pspec->flags & G_PARAM_READABLE)) + { + g_warning ("%s: child property `%s' of container `%s' is not readable", + G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); + break; + } + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + container_get_child_property (container, actor, &value, pspec); + + G_VALUE_LCOPY (&value, var_args, 0, &error); + if (error) + { + g_warning ("%s: %s", G_STRLOC, error); + g_free (error); + g_value_unset (&value); + break; + } + + g_value_unset (&value); + + name = va_arg (var_args, gchar*); + } + + va_end (var_args); +} diff --git a/clutter/clutter-container.h b/clutter/clutter-container.h index 1fb541bf4..62342daf7 100644 --- a/clutter/clutter-container.h +++ b/clutter/clutter-container.h @@ -30,6 +30,8 @@ #define __CLUTTER_CONTAINER_H__ #include +#include +#include G_BEGIN_DECLS @@ -38,7 +40,6 @@ G_BEGIN_DECLS #define CLUTTER_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONTAINER)) #define CLUTTER_CONTAINER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_CONTAINER, ClutterContainerIface)) -typedef struct _ClutterContainer ClutterContainer; /* dummy */ typedef struct _ClutterContainerIface ClutterContainerIface; /** @@ -50,6 +51,15 @@ typedef struct _ClutterContainerIface ClutterContainerIface; * @lower: virtual function for lowering a child * @sort_depth_order: virtual function for sorting the children of a * container depending on their depth + * @child_record_type: The GType used for storing auxiliary information about + * each of the containers children. + * @create_child_record: virtual function that gets called for each added + * child, the function should instantiate @child_record_type, set the container + * and actor fields in the instance and add the record to a data structure for + * subsequent access for get_child_record. + * @destroy_child_record: virtual function that gets called when a child is removed + * it shuld release all resources held by the record. + * @get_child_record: return the record for a container child. * @actor_added: signal class handler for ClutterContainer::actor_added * @actor_removed: signal class handler for ClutterContainer::actor_removed * @@ -63,26 +73,40 @@ struct _ClutterContainerIface GTypeInterface g_iface; /*< public >*/ - void (* add) (ClutterContainer *container, - ClutterActor *actor); - void (* remove) (ClutterContainer *container, - ClutterActor *actor); - void (* foreach) (ClutterContainer *container, - ClutterCallback callback, - gpointer user_data); - void (* raise) (ClutterContainer *container, - ClutterActor *actor, - ClutterActor *sibling); - void (* lower) (ClutterContainer *container, - ClutterActor *actor, - ClutterActor *sibling); - void (* sort_depth_order) (ClutterContainer *container); + void (* add) (ClutterContainer *container, + ClutterActor *actor); + void (* remove) (ClutterContainer *container, + ClutterActor *actor); + void (* foreach) (ClutterContainer *container, + ClutterCallback callback, + gpointer user_data); + void (* raise) (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); + void (* lower) (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); + void (* sort_depth_order) (ClutterContainer *container); + + /* ClutterChildMeta management */ + + GType child_meta_type; + void (* create_child_meta) (ClutterContainer *container, + ClutterActor *actor); + void (* destroy_child_meta) (ClutterContainer *container, + ClutterActor *actor); + ClutterChildMeta *(* get_child_meta) (ClutterContainer *container, + ClutterActor *actor); /* signals */ void (* actor_added) (ClutterContainer *container, ClutterActor *actor); void (* actor_removed) (ClutterContainer *container, ClutterActor *actor); + + void (* child_notify) (ClutterContainer *container, + ClutterActor *actor, + GParamSpec *pspec); }; GType clutter_container_get_type (void) G_GNUC_CONST; @@ -107,15 +131,43 @@ GList * clutter_container_get_children (ClutterContainer *container); void clutter_container_foreach (ClutterContainer *container, ClutterCallback callback, gpointer user_data); -ClutterActor *clutter_container_find_child_by_name(ClutterContainer *container, - const gchar *child_name); -void clutter_container_raise_child (ClutterContainer *container, - ClutterActor *actor, - ClutterActor *sibling); -void clutter_container_lower_child (ClutterContainer *container, - ClutterActor *actor, - ClutterActor *sibling); -void clutter_container_sort_depth_order (ClutterContainer *container); +ClutterActor *clutter_container_find_child_by_name (ClutterContainer *container, + const gchar *child_name); +void clutter_container_raise_child (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); +void clutter_container_lower_child (ClutterContainer *container, + ClutterActor *actor, + ClutterActor *sibling); +void clutter_container_sort_depth_order (ClutterContainer *container); + + + +GParamSpec * clutter_container_class_find_child_property (GObjectClass *klass, + const gchar *property_name); +GParamSpec ** clutter_container_class_list_child_properties (GObjectClass *klass, + guint *n_properties); + +ClutterChildMeta *clutter_container_get_child_meta (ClutterContainer *container, + ClutterActor *actor); + +void clutter_container_child_set_property (ClutterContainer *container, + ClutterActor *child, + const gchar * property, + const GValue *value); +void clutter_container_child_get_property (ClutterContainer *container, + ClutterActor *child, + const gchar *property, + GValue *value); +void clutter_container_child_set (ClutterContainer *container, + ClutterActor *actor, + const gchar *first_prop, + ...) G_GNUC_NULL_TERMINATED; +void clutter_container_child_get (ClutterContainer *container, + ClutterActor *actor, + const gchar *first_prop, + ...) G_GNUC_NULL_TERMINATED; + G_END_DECLS diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list index 31f0842a1..6bfb8ccd1 100644 --- a/clutter/clutter-marshal.list +++ b/clutter/clutter-marshal.list @@ -1,12 +1,13 @@ -VOID:INT64,INT64,FLOAT,BOOLEAN -VOID:STRING,BOOLEAN,BOOLEAN -VOID:INT -VOID:INT,INT -VOID:INT,INT,INT,INT -VOID:BOXED -VOID:OBJECT -VOID:VOID -VOID:OBJECT,POINTER -VOID:STRING,UINT BOOLEAN:BOXED UINT:VOID +VOID:BOXED +VOID:INT +VOID:INT64,INT64,FLOAT,BOOLEAN +VOID:INT,INT +VOID:INT,INT,INT,INT +VOID:OBJECT +VOID:OBJECT,OBJECT,PARAM +VOID:OBJECT,POINTER +VOID:STRING,BOOLEAN,BOOLEAN +VOID:STRING,UINT +VOID:VOID diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h index 0a096113c..a15c87119 100644 --- a/clutter/clutter-types.h +++ b/clutter/clutter-types.h @@ -38,8 +38,10 @@ G_BEGIN_DECLS #define CLUTTER_TYPE_VERTEX (clutter_vertex_get_type ()) /* Forward delarations to avoid header catch 22's */ -typedef struct _ClutterActor ClutterActor; -typedef struct _ClutterStage ClutterStage; +typedef struct _ClutterActor ClutterActor; +typedef struct _ClutterStage ClutterStage; +typedef struct _ClutterContainer ClutterContainer; /* dummy */ +typedef struct _ClutterChildMeta ClutterChildMeta; /** * ClutterGravity: diff --git a/doc/reference/clutter/clutter-docs.sgml b/doc/reference/clutter/clutter-docs.sgml index 8a4c28ab4..3ec0a6349 100644 --- a/doc/reference/clutter/clutter-docs.sgml +++ b/doc/reference/clutter/clutter-docs.sgml @@ -327,6 +327,7 @@ Abstract classes and interfaces + diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 9214df3f5..e96906134 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -247,10 +247,24 @@ clutter_container_remove clutter_container_remove_valist clutter_container_get_children clutter_container_foreach + + clutter_container_find_child_by_name clutter_container_raise_child clutter_container_lower_child clutter_container_sort_depth_order + + +clutter_container_find_child_property +clutter_container_list_child_properties +clutter_container_child_set_property +clutter_container_child_get_property +clutter_container_child_set +clutter_container_child_get + + +clutter_container_get_child_data + CLUTTER_TYPE_CONTAINER CLUTTER_CONTAINER @@ -260,6 +274,24 @@ CLUTTER_CONTAINER_GET_IFACE clutter_container_get_type +
+clutter-child-data +ClutterChildData<TITLE> +ClutterChildData +ClutterChildDataClass +clutter_child_data_get_container +clutter_child_data_get_actor +<SUBSECTION Standard> +CLUTTER_TYPE_CHILD_DATA +CLUTTER_CHILD_DATA +CLUTTER_IS_CHILD_DATA +CLUTTER_CHILD_DATA_CLASS +CLUTTER_IS_CHILD_DATA_CLASS +CLUTTER_CHILD_DATA_GET_CLASS +<SUBSECTION Private> +clutter_child_data_get_type +</SECTION> + <SECTION> <FILE>clutter-rectangle</FILE> <TITLE>ClutterRectangle