2c36b81996
The prerequisite for ClutterContainer in 1.x is GObject, and we cannot restrict the prerequisites without an ABI break.
1229 lines
36 KiB
C
1229 lines
36 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
*
|
|
* Copyright (C) 2006 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
*
|
|
* ClutterContainer: Generic actor container interface.
|
|
* Author: Emmanuele Bassi <ebassi@openedhand.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <glib-object.h>
|
|
#include <gobject/gvaluecollector.h>
|
|
|
|
#include "clutter-container.h"
|
|
#include "clutter-child-meta.h"
|
|
|
|
#include "clutter-debug.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
#include "clutter-enum-types.h"
|
|
|
|
#define CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED(container,vfunc) \
|
|
G_STMT_START { \
|
|
g_warning ("Container of type '%s' does not implement " \
|
|
"the required ClutterContainer::%s virtual " \
|
|
"function.", \
|
|
G_OBJECT_TYPE_NAME ((container)), \
|
|
(vfunc)); \
|
|
} G_STMT_END
|
|
|
|
#define CLUTTER_CONTAINER_NOTE_NOT_IMPLEMENTED(container,vfunc) \
|
|
G_STMT_START { \
|
|
CLUTTER_NOTE (ACTOR, "Container of type '%s' does not " \
|
|
"implement the ClutterContainer::%s " \
|
|
"virtual function.", \
|
|
G_OBJECT_TYPE_NAME ((container)), \
|
|
(vfunc)); \
|
|
} G_STMT_END
|
|
|
|
/**
|
|
* SECTION:clutter-container
|
|
* @short_description: An interface for implementing container actors
|
|
*
|
|
* #ClutterContainer is an interface for writing actors containing other
|
|
* #ClutterActor<!-- -->s. It provides a standard API for adding, removing
|
|
* and iterating on every contained actor.
|
|
*
|
|
* An actor implementing #ClutterContainer is #ClutterGroup.
|
|
*
|
|
* #ClutterContainer is available since Clutter 0.4
|
|
*/
|
|
|
|
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 child_notify (ClutterContainer *container,
|
|
ClutterActor *child,
|
|
GParamSpec *pspec);
|
|
|
|
typedef ClutterContainerIface ClutterContainerInterface;
|
|
|
|
G_DEFINE_INTERFACE (ClutterContainer, clutter_container, G_TYPE_OBJECT);
|
|
|
|
static void
|
|
clutter_container_default_init (ClutterContainerInterface *iface)
|
|
{
|
|
GType iface_type = G_TYPE_FROM_INTERFACE (iface);
|
|
|
|
quark_child_meta =
|
|
g_quark_from_static_string ("clutter-container-child-data");
|
|
|
|
/**
|
|
* ClutterContainer::actor-added:
|
|
* @container: the actor which received the signal
|
|
* @actor: the new child that has been added to @container
|
|
*
|
|
* The ::actor-added signal is emitted each time an actor
|
|
* has been added to @container.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
container_signals[ACTOR_ADDED] =
|
|
g_signal_new (I_("actor-added"),
|
|
iface_type,
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (ClutterContainerIface, actor_added),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
/**
|
|
* ClutterContainer::actor-removed:
|
|
* @container: the actor which received the signal
|
|
* @actor: the child that has been removed from @container
|
|
*
|
|
* The ::actor-removed signal is emitted each time an actor
|
|
* is removed from @container.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
container_signals[ACTOR_REMOVED] =
|
|
g_signal_new (I_("actor-removed"),
|
|
iface_type,
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (ClutterContainerIface, actor_removed),
|
|
NULL, NULL,
|
|
_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
|
|
* @pspec: the #GParamSpec of the 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_SIGNAL_DETAILED,
|
|
G_STRUCT_OFFSET (ClutterContainerIface, child_notify),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__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;
|
|
iface->child_notify = child_notify;
|
|
}
|
|
|
|
/**
|
|
* clutter_container_add: (skip)
|
|
* @container: a #ClutterContainer
|
|
* @first_actor: the first #ClutterActor to add
|
|
* @Varargs: %NULL terminated list of actors to add
|
|
*
|
|
* Adds a list of #ClutterActor<!-- -->s to @container. Each time and
|
|
* actor is added, the "actor-added" signal is emitted. Each actor should
|
|
* be parented to @container, which takes a reference on the actor. You
|
|
* cannot add a #ClutterActor to more than one #ClutterContainer.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_add (ClutterContainer *container,
|
|
ClutterActor *first_actor,
|
|
...)
|
|
{
|
|
va_list args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (first_actor));
|
|
|
|
va_start (args, first_actor);
|
|
clutter_container_add_valist (container, first_actor, args);
|
|
va_end (args);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_add_actor:
|
|
* @container: a #ClutterContainer
|
|
* @actor: the first #ClutterActor to add
|
|
*
|
|
* Adds a #ClutterActor to @container. This function will emit the
|
|
* "actor-added" signal. The actor should be parented to
|
|
* @container. You cannot add a #ClutterActor to more than one
|
|
* #ClutterContainer.
|
|
*
|
|
* Virtual: add
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_add_actor (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
ClutterActor *parent;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->add)
|
|
{
|
|
CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED (container, "add");
|
|
return;
|
|
}
|
|
|
|
parent = clutter_actor_get_parent (actor);
|
|
if (parent)
|
|
{
|
|
g_warning ("Attempting to add actor of type '%s' to a "
|
|
"container of type '%s', but the actor has "
|
|
"already a parent of type '%s'.",
|
|
g_type_name (G_OBJECT_TYPE (actor)),
|
|
g_type_name (G_OBJECT_TYPE (container)),
|
|
g_type_name (G_OBJECT_TYPE (parent)));
|
|
return;
|
|
}
|
|
|
|
clutter_container_create_child_meta (container, actor);
|
|
|
|
iface->add (container, actor);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_add_valist: (skip)
|
|
* @container: a #ClutterContainer
|
|
* @first_actor: the first #ClutterActor to add
|
|
* @var_args: list of actors to add, followed by %NULL
|
|
*
|
|
* Alternative va_list version of clutter_container_add().
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_add_valist (ClutterContainer *container,
|
|
ClutterActor *first_actor,
|
|
va_list var_args)
|
|
{
|
|
ClutterActor *actor;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (first_actor));
|
|
|
|
actor = first_actor;
|
|
while (actor)
|
|
{
|
|
clutter_container_add_actor (container, actor);
|
|
actor = va_arg (var_args, ClutterActor*);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_container_remove: (skip)
|
|
* @container: a #ClutterContainer
|
|
* @first_actor: first #ClutterActor to remove
|
|
* @Varargs: a %NULL-terminated list of actors to remove
|
|
*
|
|
* Removes a %NULL terminated list of #ClutterActor<!-- -->s from
|
|
* @container. Each actor should be unparented, so if you want to keep it
|
|
* around you must hold a reference to it yourself, using g_object_ref().
|
|
* Each time an actor is removed, the "actor-removed" signal is
|
|
* emitted by @container.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_remove (ClutterContainer *container,
|
|
ClutterActor *first_actor,
|
|
...)
|
|
{
|
|
va_list var_args;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (first_actor));
|
|
|
|
va_start (var_args, first_actor);
|
|
clutter_container_remove_valist (container, first_actor, var_args);
|
|
va_end (var_args);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_remove_actor:
|
|
* @container: a #ClutterContainer
|
|
* @actor: a #ClutterActor
|
|
*
|
|
* Removes @actor from @container. The actor should be unparented, so
|
|
* if you want to keep it around you must hold a reference to it
|
|
* yourself, using g_object_ref(). When the actor has been removed,
|
|
* the "actor-removed" signal is emitted by @container.
|
|
*
|
|
* Virtual: remove
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_remove_actor (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
ClutterActor *parent;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->remove)
|
|
{
|
|
CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED (container, "remove");
|
|
return;
|
|
}
|
|
|
|
parent = clutter_actor_get_parent (actor);
|
|
if (parent != CLUTTER_ACTOR (container))
|
|
{
|
|
g_warning ("Attempting to remove actor of type '%s' from "
|
|
"group of class '%s', but the container is not "
|
|
"the actor's parent.",
|
|
g_type_name (G_OBJECT_TYPE (actor)),
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
return;
|
|
}
|
|
|
|
clutter_container_destroy_child_meta (container, actor);
|
|
|
|
iface->remove (container, actor);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_remove_valist: (skip)
|
|
* @container: a #ClutterContainer
|
|
* @first_actor: the first #ClutterActor to add
|
|
* @var_args: list of actors to remove, followed by %NULL
|
|
*
|
|
* Alternative va_list version of clutter_container_remove().
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_remove_valist (ClutterContainer *container,
|
|
ClutterActor *first_actor,
|
|
va_list var_args)
|
|
{
|
|
ClutterActor *actor;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (first_actor));
|
|
|
|
actor = first_actor;
|
|
while (actor)
|
|
{
|
|
clutter_container_remove_actor (container, actor);
|
|
actor = va_arg (var_args, ClutterActor*);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_children_cb (ClutterActor *child,
|
|
gpointer data)
|
|
{
|
|
GList **children = data;
|
|
|
|
*children = g_list_prepend (*children, child);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_get_children:
|
|
* @container: a #ClutterContainer
|
|
*
|
|
* Retrieves all the children of @container.
|
|
*
|
|
* Return value: (element-type Clutter.Actor) (transfer container): a list
|
|
* of #ClutterActor<!-- -->s. Use g_list_free() on the returned
|
|
* list when done.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
GList *
|
|
clutter_container_get_children (ClutterContainer *container)
|
|
{
|
|
GList *retval;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL);
|
|
|
|
retval = NULL;
|
|
clutter_container_foreach (container, get_children_cb, &retval);
|
|
|
|
return g_list_reverse (retval);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_foreach:
|
|
* @container: a #ClutterContainer
|
|
* @callback: (scope call): a function to be called for each child
|
|
* @user_data: data to be passed to the function, or %NULL
|
|
*
|
|
* Calls @callback for each child of @container that was added
|
|
* by the application (with clutter_container_add_actor()). Does
|
|
* not iterate over "internal" children that are part of the
|
|
* container's own implementation, if any.
|
|
*
|
|
* Since: 0.4
|
|
*/
|
|
void
|
|
clutter_container_foreach (ClutterContainer *container,
|
|
ClutterCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->foreach)
|
|
{
|
|
CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED (container, "foreach");
|
|
return;
|
|
}
|
|
|
|
iface->foreach (container, callback, user_data);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_foreach_with_internals:
|
|
* @container: a #ClutterContainer
|
|
* @callback: (scope call): a function to be called for each child
|
|
* @user_data: data to be passed to the function, or %NULL
|
|
*
|
|
* Calls @callback for each child of @container, including "internal"
|
|
* children built in to the container itself that were never added
|
|
* by the application.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_container_foreach_with_internals (ClutterContainer *container,
|
|
ClutterCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (callback != NULL);
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->foreach)
|
|
{
|
|
CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED (container, "foreach");
|
|
return;
|
|
}
|
|
|
|
if (iface->foreach_with_internals != NULL)
|
|
iface->foreach_with_internals (container, callback, user_data);
|
|
else
|
|
iface->foreach (container, callback, user_data);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_raise_child:
|
|
* @container: a #ClutterContainer
|
|
* @actor: the actor to raise
|
|
* @sibling: the sibling to raise to, or %NULL to raise to the top
|
|
*
|
|
* Raises @actor to @sibling level, in the depth ordering.
|
|
*
|
|
* Virtual: raise
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_container_raise_child (ClutterContainer *container,
|
|
ClutterActor *actor,
|
|
ClutterActor *sibling)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->raise)
|
|
{
|
|
CLUTTER_CONTAINER_NOTE_NOT_IMPLEMENTED (container, "raise");
|
|
return;
|
|
};
|
|
|
|
if (actor == sibling)
|
|
return;
|
|
|
|
if (clutter_actor_get_parent (actor) != CLUTTER_ACTOR (container))
|
|
{
|
|
g_warning ("Actor of type '%s' is not a child of the container "
|
|
"of type '%s'",
|
|
g_type_name (G_OBJECT_TYPE (actor)),
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
return;
|
|
}
|
|
|
|
if (sibling &&
|
|
clutter_actor_get_parent (sibling) != CLUTTER_ACTOR (container))
|
|
{
|
|
g_warning ("Actor of type '%s' is not a child of the container "
|
|
"of type '%s'",
|
|
g_type_name (G_OBJECT_TYPE (sibling)),
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
return;
|
|
}
|
|
|
|
iface->raise (container, actor, sibling);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_lower_child:
|
|
* @container: a #ClutterContainer
|
|
* @actor: the actor to raise
|
|
* @sibling: the sibling to lower to, or %NULL to lower to the bottom
|
|
*
|
|
* Lowers @actor to @sibling level, in the depth ordering.
|
|
*
|
|
* Virtual: lower
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_container_lower_child (ClutterContainer *container,
|
|
ClutterActor *actor,
|
|
ClutterActor *sibling)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling));
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (!iface->lower)
|
|
{
|
|
CLUTTER_CONTAINER_NOTE_NOT_IMPLEMENTED (container, "lower");
|
|
return;
|
|
}
|
|
|
|
if (actor == sibling)
|
|
return;
|
|
|
|
if (clutter_actor_get_parent (actor) != CLUTTER_ACTOR (container))
|
|
{
|
|
g_warning ("Actor of type '%s' is not a child of the container "
|
|
"of type '%s'",
|
|
g_type_name (G_OBJECT_TYPE (actor)),
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
return;
|
|
}
|
|
|
|
if (sibling &&
|
|
clutter_actor_get_parent (sibling) != CLUTTER_ACTOR (container))
|
|
{
|
|
g_warning ("Actor of type '%s' is not a child of the container "
|
|
"of type '%s'",
|
|
g_type_name (G_OBJECT_TYPE (sibling)),
|
|
g_type_name (G_OBJECT_TYPE (container)));
|
|
return;
|
|
}
|
|
|
|
iface->lower (container, actor, sibling);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_sort_depth_order:
|
|
* @container: a #ClutterContainer
|
|
*
|
|
* Sorts a container's children using their depth. This function should not
|
|
* be normally used by applications.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
void
|
|
clutter_container_sort_depth_order (ClutterContainer *container)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
|
|
iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
if (iface->sort_depth_order)
|
|
iface->sort_depth_order (container);
|
|
else
|
|
CLUTTER_CONTAINER_NOTE_NOT_IMPLEMENTED (container, "sort_depth_order");
|
|
}
|
|
|
|
/**
|
|
* clutter_container_find_child_by_name:
|
|
* @container: a #ClutterContainer
|
|
* @child_name: the name of the requested child.
|
|
*
|
|
* Finds a child actor of a container by its name. Search recurses
|
|
* into any child container.
|
|
*
|
|
* Return value: (transfer none): The child actor with the requested name,
|
|
* or %NULL if no actor with that name was found.
|
|
*
|
|
* Since: 0.6
|
|
*/
|
|
ClutterActor *
|
|
clutter_container_find_child_by_name (ClutterContainer *container,
|
|
const gchar *child_name)
|
|
{
|
|
GList *children;
|
|
GList *iter;
|
|
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))
|
|
{
|
|
ClutterActor *a;
|
|
const gchar *iter_name;
|
|
|
|
a = CLUTTER_ACTOR (iter->data);
|
|
iter_name = clutter_actor_get_name (a);
|
|
|
|
if (iter_name && !strcmp (iter_name, child_name))
|
|
{
|
|
actor = a;
|
|
break;
|
|
}
|
|
|
|
if (CLUTTER_IS_CONTAINER (a))
|
|
{
|
|
ClutterContainer *c = CLUTTER_CONTAINER (a);
|
|
|
|
actor = clutter_container_find_child_by_name (c, child_name);
|
|
if (actor)
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_list_free (children);
|
|
|
|
return actor;
|
|
}
|
|
|
|
static ClutterChildMeta *
|
|
get_child_meta (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
ClutterChildMeta *meta;
|
|
|
|
if (iface->child_meta_type == G_TYPE_INVALID)
|
|
return NULL;
|
|
|
|
meta = g_object_get_qdata (G_OBJECT (actor), quark_child_meta);
|
|
if (meta != NULL && meta->actor == actor)
|
|
return meta;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
create_child_meta (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
ClutterChildMeta *child_meta = 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,
|
|
"container", container,
|
|
"actor", actor,
|
|
NULL);
|
|
|
|
g_object_set_qdata_full (G_OBJECT (actor), quark_child_meta,
|
|
child_meta,
|
|
(GDestroyNotify) g_object_unref);
|
|
}
|
|
|
|
static void
|
|
destroy_child_meta (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container);
|
|
|
|
if (iface->child_meta_type == G_TYPE_INVALID)
|
|
return;
|
|
|
|
g_object_set_qdata (G_OBJECT (actor), quark_child_meta, NULL);
|
|
}
|
|
|
|
/**
|
|
* 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: (transfer none): 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 #ClutterChildMeta<!-- -->s
|
|
*
|
|
* 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.
|
|
*
|
|
* This function is only useful when adding a #ClutterActor to
|
|
* a #ClutterContainer implementation outside of the
|
|
* #ClutterContainer::add() virtual function implementation.
|
|
*
|
|
* Applications should not call this function.
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_container_create_child_meta (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
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.
|
|
*
|
|
* This function is only useful when removing a #ClutterActor to
|
|
* a #ClutterContainer implementation outside of the
|
|
* #ClutterContainer::add() virtual function implementation.
|
|
*
|
|
* Applications should not call this function.
|
|
*
|
|
* Since: 1.2
|
|
*/
|
|
void
|
|
clutter_container_destroy_child_meta (ClutterContainer *container,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterContainerIface *iface;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
|
|
|
|
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: (transfer none): 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: (array length=n_properties) (transfer full): an array
|
|
* of #GParamSpec<!-- -->s 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 void
|
|
child_notify (ClutterContainer *container,
|
|
ClutterActor *actor,
|
|
GParamSpec *pspec)
|
|
{
|
|
}
|
|
|
|
static inline void
|
|
container_set_child_property (ClutterContainer *container,
|
|
ClutterActor *actor,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterChildMeta *data;
|
|
|
|
data = clutter_container_get_child_meta (container, actor);
|
|
g_object_set_property (G_OBJECT (data), pspec->name, value);
|
|
|
|
g_signal_emit (container, container_signals[CHILD_NOTIFY],
|
|
(pspec->flags & G_PARAM_STATIC_NAME)
|
|
? g_quark_from_static_string (pspec->name)
|
|
: g_quark_from_string (pspec->name),
|
|
actor, pspec);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_child_set_property:
|
|
* @container: a #ClutterContainer
|
|
* @child: 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 *child,
|
|
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 (child));
|
|
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, child, 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_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec),
|
|
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_get_property (G_OBJECT (data), pspec->name, value);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_child_get_property:
|
|
* @container: a #ClutterContainer
|
|
* @child: 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 *child,
|
|
const gchar *property,
|
|
GValue *value)
|
|
{
|
|
GObjectClass *klass;
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
|
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, child, 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);
|
|
}
|
|
|
|
/**
|
|
* clutter_container_child_notify:
|
|
* @container: a #ClutterContainer
|
|
* @child: a #ClutterActor
|
|
* @pspec: a #GParamSpec
|
|
*
|
|
* Calls the <function>child_notify()</function> virtual function of
|
|
* #ClutterContainer. The default implementation will emit the
|
|
* #ClutterContainer::child-notify signal.
|
|
*
|
|
* Since: 1.6
|
|
*/
|
|
void
|
|
clutter_container_child_notify (ClutterContainer *container,
|
|
ClutterActor *child,
|
|
GParamSpec *pspec)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
|
|
g_return_if_fail (CLUTTER_IS_ACTOR (child));
|
|
g_return_if_fail (pspec != NULL);
|
|
|
|
g_return_if_fail (clutter_actor_get_parent (child) == CLUTTER_ACTOR (container));
|
|
|
|
CLUTTER_CONTAINER_GET_IFACE (container)->child_notify (container,
|
|
child,
|
|
pspec);
|
|
}
|