From 89ad3d0bb4522b3842dae6e533012f0e06ca7ba1 Mon Sep 17 00:00:00 2001 From: Nicola Fontana Date: Sat, 25 Oct 2008 21:26:49 +0200 Subject: [PATCH] [adg-container.* adg-entity.*] Merged gcontainer in AdgContainer & AdgEntity As required to drop the libgcontainer dependency, the gcontainer sources have been included inside AdgContainer (GContainerable) and AdgEntity (GChildable). --- adg/adg-container.c | 399 +++++++++++++++++++++++++++++++++++++++++------ adg/adg-container.h | 48 +++++- adg/adg-entity-private.h | 4 +- adg/adg-entity.c | 253 ++++++++++++++++++++++-------- adg/adg-entity.h | 14 ++ 5 files changed, 593 insertions(+), 125 deletions(-) diff --git a/adg/adg-container.c b/adg/adg-container.c index 3ceda9a1..718ef5d8 100644 --- a/adg/adg-container.c +++ b/adg/adg-container.c @@ -23,7 +23,7 @@ * @title: AdgContainer * @short_description: Base class for entity that can contain other entities * - * The #AdgContainer is an entity that implements the #GContainerable interface. + * The #AdgContainer is an entity that can contains more sub-entities. * Each AdgContainer has its paper matrix (#AdgContainer:paper_matrix) to be * used on paper-dependent references (such as font and arrow sizes, line * thickness etc...) and a model matrix usually applied to the model view. See @@ -49,8 +49,13 @@ enum { PROP_PAPER_TRANSFORMATION }; +enum { + ADD, + REMOVE, + LAST_SIGNAL +}; + -static void containerable_init (GContainerableIface *iface); static void get_property (GObject *object, guint prop_id, GValue *value, @@ -59,26 +64,32 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void dispose (GObject *object); static const AdgMatrix *get_model_matrix (AdgEntity *entity); static const AdgMatrix *get_paper_matrix (AdgEntity *entity); static void model_matrix_changed (AdgEntity *entity, AdgMatrix *parent_matrix); static void paper_matrix_changed (AdgEntity *entity, AdgMatrix *parent_matrix); -static GSList * get_children (GContainerable *containerable); -static gboolean add (GContainerable *containerable, - GChildable *childable); -static gboolean remove (GContainerable *containerable, - GChildable *childable); +static GSList * get_children (AdgContainer *container); +static gboolean add (AdgContainer *container, + AdgEntity *entity); +static void real_add (AdgContainer *container, + AdgEntity *entity, + gpointer user_data); +static gboolean remove (AdgContainer *container, + AdgEntity *entity); +static void real_remove (AdgContainer *container, + AdgEntity *entity, + gpointer user_data); static void invalidate (AdgEntity *entity); static void render (AdgEntity *entity, cairo_t *cr); +static guint signals[LAST_SIGNAL] = { 0 }; + -G_DEFINE_TYPE_EXTENDED(AdgContainer, adg_container, - ADG_TYPE_ENTITY, 0, - G_IMPLEMENT_INTERFACE(G_TYPE_CONTAINERABLE, - containerable_init)) +G_DEFINE_TYPE(AdgContainer, adg_container, ADG_TYPE_ENTITY) static void @@ -87,6 +98,8 @@ adg_container_class_init(AdgContainerClass *klass) GObjectClass *gobject_class; AdgEntityClass *entity_class; GParamSpec *param; + GClosure *closure; + GType param_types[1]; gobject_class = (GObjectClass *) klass; entity_class = (AdgEntityClass *) klass; @@ -95,7 +108,7 @@ adg_container_class_init(AdgContainerClass *klass) gobject_class->get_property = get_property; gobject_class->set_property = set_property; - gobject_class->dispose = g_containerable_dispose; + gobject_class->dispose = dispose; entity_class->model_matrix_changed = model_matrix_changed; entity_class->paper_matrix_changed = paper_matrix_changed; @@ -104,7 +117,15 @@ adg_container_class_init(AdgContainerClass *klass) entity_class->invalidate = invalidate; entity_class->render = render; - g_object_class_override_property(gobject_class, PROP_CHILD, "child"); + klass->get_children = get_children; + klass->add = add; + klass->remove = remove; + + param = g_param_spec_boxed("child", + P_("Child"), + P_("Can be used to add a new child to the container"), + ADG_TYPE_ENTITY, G_PARAM_WRITABLE); + g_object_class_install_property(gobject_class, PROP_CHILD, param); param = g_param_spec_boxed("model-transformation", P_("The model transformation"), @@ -119,14 +140,34 @@ adg_container_class_init(AdgContainerClass *klass) ADG_TYPE_MATRIX, G_PARAM_READWRITE); g_object_class_install_property(gobject_class, PROP_PAPER_TRANSFORMATION, param); -} -static void -containerable_init(GContainerableIface *iface) -{ - iface->get_children = get_children; - iface->add = add; - iface->remove = remove; + /** + * AdgContainer::add: + * @container: an #AdgContainer + * @entity: the #AdgEntity to add + * + * Adds @entity to @container. + **/ + closure = g_cclosure_new(G_CALLBACK(real_add), (gpointer)0xdeadbeaf, NULL); + param_types[0] = G_TYPE_OBJECT; + signals[ADD] = g_signal_newv("add", ADG_TYPE_CONTAINER, + G_SIGNAL_RUN_FIRST, closure, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, param_types); + + /** + * AdgContainer::remove: + * @container: an #AdgContainer + * @entity: the #AdgEntity to remove + * + * Removes @entity from @container. + **/ + closure = g_cclosure_new(G_CALLBACK(real_remove), (gpointer)0xdeadbeaf, NULL); + param_types[0] = G_TYPE_OBJECT; + signals[REMOVE] = g_signal_newv("remove", ADG_TYPE_CONTAINER, + G_SIGNAL_RUN_FIRST, closure, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, param_types); } static void @@ -171,8 +212,7 @@ set_property(GObject *object, switch (prop_id) { case PROP_CHILD: - g_containerable_add((GContainerable *) container, - g_value_get_object(value)); + adg_container_add(container, g_value_get_object(value)); break; case PROP_MODEL_TRANSFORMATION: adg_matrix_set(&container->priv->model_transformation, @@ -187,42 +227,80 @@ set_property(GObject *object, } } +static void +dispose(GObject *object) +{ + AdgContainer *container = (AdgContainer *) object; + + adg_container_foreach(container, G_CALLBACK(adg_entity_unparent), NULL); + + ((GObjectClass *) PARENT_CLASS)->dispose(object); +} + static GSList * -get_children(GContainerable *containerable) +get_children(AdgContainer *container) { - AdgContainer *container = (AdgContainer *) containerable; - return g_slist_copy(container->priv->children); } static gboolean -add(GContainerable *containerable, GChildable *childable) +add(AdgContainer *container, AdgEntity *entity) { - AdgContainer *container = (AdgContainer *) containerable; - - container->priv->children = - g_slist_append(container->priv->children, childable); + container->priv->children = g_slist_append(container->priv->children, + entity); return TRUE; } -static gboolean -remove(GContainerable *containerable, GChildable *childable) +static void +real_add(AdgContainer *container, AdgEntity *entity, gpointer user_data) { - AdgContainer *container; - GSList *node; + AdgContainer *old_parent; + + g_assert(user_data == (gpointer) 0xdeadbeaf); + + old_parent = adg_entity_get_parent(entity); + + if (old_parent != NULL) { + g_warning("Attempting to add an object with type %s to a container " + "of type %s, but the object is already inside a container " + "of type %s.", + g_type_name(G_OBJECT_TYPE(entity)), + g_type_name(G_OBJECT_TYPE(container)), + g_type_name(G_OBJECT_TYPE(old_parent))); + return; + } - container = (AdgContainer *) containerable; - node = g_slist_find(container->priv->children, childable); + if (ADG_CONTAINER_GET_CLASS(container)->add(container, entity)) + adg_entity_set_parent(entity, container); + else + g_signal_stop_emission(container, signals[ADD], 0); +} + +static gboolean +remove(AdgContainer *container, AdgEntity *entity) +{ + GSList *node = g_slist_find(container->priv->children, entity); if (!node) return FALSE; - container->priv->children = - g_slist_delete_link(container->priv->children, node); + container->priv->children = g_slist_delete_link(container->priv->children, + node); return TRUE; } +static void +real_remove(AdgContainer *container, AdgEntity *entity, gpointer user_data) +{ + g_assert(user_data == (gpointer) 0xdeadbeaf); + + if (ADG_CONTAINER_GET_CLASS(container)->remove(container, entity)) + adg_entity_unparent(entity); + else + g_signal_stop_emission(container, signals[REMOVE], 0); +} + static void model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix) @@ -239,9 +317,8 @@ model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix) adg_matrix_set(&container->priv->model_matrix, &container->priv->model_transformation); - g_containerable_propagate_by_name((GContainerable *) entity, - "model-matrix-changed", - &container->priv->model_matrix); + adg_container_propagate_by_name(container, "model-matrix-changed", + &container->priv->model_matrix); } static void @@ -259,41 +336,236 @@ paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix) adg_matrix_set(&container->priv->paper_matrix, &container->priv->paper_transformation); - g_containerable_propagate_by_name((GContainerable *) entity, - "paper-matrix-changed", - &container->priv->paper_matrix); + adg_container_propagate_by_name(container, "paper-matrix-changed", + &container->priv->paper_matrix); } static const AdgMatrix * get_model_matrix(AdgEntity *entity) { - return &((AdgContainer *) entity)->priv->model_matrix; + AdgContainer *container = (AdgContainer *) entity; + + return &container->priv->model_matrix; } static const AdgMatrix * get_paper_matrix(AdgEntity *entity) { - return &((AdgContainer *) entity)->priv->paper_matrix; + AdgContainer *container = (AdgContainer *) entity; + + return &container->priv->paper_matrix; } static void invalidate(AdgEntity *entity) { - g_containerable_propagate_by_name((GContainerable *) entity, - "invalidate", NULL); + adg_container_propagate_by_name((AdgContainer *) entity, "invalidate"); + PARENT_CLASS->invalidate(entity); } static void render(AdgEntity *entity, cairo_t *cr) { cairo_set_matrix(cr, adg_entity_get_model_matrix(entity)); - g_containerable_propagate_by_name((GContainerable *) entity, - "render", cr); + adg_container_propagate_by_name((AdgContainer *) entity, "render", cr); PARENT_CLASS->render(entity, cr); } +/** + * adg_container_add: + * @container: an #AdgContainer + * @entity: an #AdgEntity + * + * Emits a #AdgContainer::add signal on @container passing + * @entity as argument. + * + * @entity may be added to only one container at a time; you can't + * place the same entity inside two different containers. + **/ +void +adg_container_add(AdgContainer *container, AdgEntity *entity) +{ + g_return_if_fail(ADG_IS_CONTAINER(container)); + g_return_if_fail(ADG_IS_ENTITY(entity)); + + g_signal_emit(container, signals[ADD], 0, entity); +} + +/** + * adg_container_remove: + * @container: an #AdgContainer + * @entity: an #AdgEntity + * + * Emits a #AdgContainer::remove signal on @container passing + * @entity as argument. @entity must be inside @container. + * + * Note that @container will own a reference to @entity + * and that this may be the last reference held; so removing an + * entity from its container can destroy it. + * + * If you want to use @entity again, you need to add a reference + * to it, using g_object_ref(), before removing it from @container. + * + * If you don't want to use @entity again, it's usually more + * efficient to simply destroy it directly using g_object_unref() + * since this will remove it from the container. + **/ +void +adg_container_remove(AdgContainer *container, AdgEntity *entity) +{ + g_return_if_fail(ADG_IS_CONTAINER(container)); + g_return_if_fail(ADG_IS_ENTITY(entity)); + + g_signal_emit(container, signals[REMOVE], 0, entity); +} + +/** + * adg_container_get_children: + * @container: an #AdgContainer + * + * Gets the children list of @container. + * This list must be manually freed when no longer user. + * + * Returns: a newly allocated #GSList or %NULL on error + **/ +GSList * +adg_container_get_children(AdgContainer *container) +{ + g_return_val_if_fail(ADG_IS_CONTAINER(container), NULL); + + return ADG_CONTAINER_GET_CLASS(container)->get_children(container); +} + +/** + * adg_container_foreach: + * @container: an #AdgContainer + * @callback: a callback + * @user_data: callback user data + * + * Invokes @callback on each child of @container. + * The callback should be declared as: + * + * + * void callback(AdgEntity *entity, gpointer user_data); + * + **/ +void +adg_container_foreach(AdgContainer *container, + GCallback callback, gpointer user_data) +{ + GSList *children; + + g_return_if_fail(ADG_IS_CONTAINER(container)); + g_return_if_fail(callback != NULL); + + children = adg_container_get_children (container); + + while (children) { + if (children->data) + ((void (*) (gpointer, gpointer)) callback) (children->data, user_data); + + children = g_slist_delete_link(children, children); + } +} + +/** + * adg_container_propagate: + * @container: an #AdgContainer + * @signal_id: the signal id + * @detail: the detail + * @...: parameters to be passed to the signal, followed by a location for + * the return value. If the return type of the signal is G_TYPE_NONE, + * the return value location can be omitted. + * + * Emits the specified signal to all the children of @container + * using g_signal_emit_valist() calls. + **/ +void +adg_container_propagate(AdgContainer *container, + guint signal_id, GQuark detail, ...) +{ + va_list var_args; + + va_start(var_args, detail); + adg_container_propagate_valist(container, signal_id, detail, var_args); + va_end(var_args); +} + +/** + * adg_container_propagate_by_name: + * @container: an #AdgContainer + * @detailed_signal: a string of the form "signal-name::detail". + * @...: a list of parameters to be passed to the signal, followed by + * a location for the return value. If the return type of the signal + * is G_TYPE_NONE, the return value location can be omitted. + * + * Emits the specified signal to all the children of @container + * using g_signal_emit_valist() calls. + **/ +void +adg_container_propagate_by_name(AdgContainer *container, + const gchar *detailed_signal, ...) +{ + guint signal_id; + GQuark detail = 0; + va_list var_args; + + if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(container), + &signal_id, &detail, FALSE)) { + g_warning("%s: signal `%s' is invalid for instance `%p'", + G_STRLOC, detailed_signal, container); + return; + } + + va_start(var_args, detailed_signal); + adg_container_propagate_valist(container, signal_id, detail, var_args); + va_end(var_args); +} + +/** + * adg_container_propagate_valist: + * @container: an #AdgContainer + * @signal_id: the signal id + * @detail: the detail + * @var_args: a list of parameters to be passed to the signal, followed by a + * location for the return value. If the return type of the signal + * is G_TYPE_NONE, the return value location can be omitted. + * + * Emits the specified signal to all the children of @container + * using g_signal_emit_valist() calls. + **/ +void +adg_container_propagate_valist(AdgContainer *container, + guint signal_id, GQuark detail, va_list var_args) +{ + GSList *children; + va_list var_copy; + + g_return_if_fail(ADG_IS_CONTAINER(container)); + + children = adg_container_get_children(container); + + while (children) { + if (children->data) { + G_VA_COPY(var_copy, var_args); + g_signal_emit_valist(children->data, signal_id, detail, var_copy); + } + + children = g_slist_delete_link(children, children); + } +} + +/** + * adg_container_get_model_transformation: + * @container: an #AdgContainer + * + * Returns the transformation to be combined with the transformations of the + * parent hierarchy to get the final matrix to be applied in the model space. + * + * Return value: the model transformation + **/ const AdgMatrix * adg_container_get_model_transformation(AdgContainer *container) { @@ -301,6 +573,13 @@ adg_container_get_model_transformation(AdgContainer *container) return &container->priv->model_transformation; } +/** + * adg_container_set_model_transformation: + * @container: an #AdgContainer + * @transformation: the new model transformation + * + * Sets the transformation to be applied in model space. + **/ void adg_container_set_model_transformation(AdgContainer *container, AdgMatrix *transformation) @@ -313,13 +592,22 @@ adg_container_set_model_transformation(AdgContainer *container, g_return_if_fail(transformation != NULL); entity = (AdgEntity *) container; - parent = (AdgEntity *) g_childable_get_parent((GChildable *) entity); + parent = (AdgEntity *) adg_entity_get_parent(entity); parent_matrix = parent ? adg_entity_get_model_matrix(parent) : NULL; adg_matrix_set(&container->priv->model_transformation, transformation); adg_entity_model_matrix_changed(entity, parent_matrix); } +/** + * adg_container_get_paper_transformation: + * @container: an #AdgContainer + * + * Returns the transformation to be combined with the transformations of the + * parent hierarchy to get the final matrix to be applied in the paper space. + * + * Return value: the paper transformation + **/ const AdgMatrix * adg_container_get_paper_transformation(AdgContainer *container) { @@ -327,6 +615,13 @@ adg_container_get_paper_transformation(AdgContainer *container) return &container->priv->paper_transformation; } +/** + * adg_container_set_paper_transformation: + * @container: an #AdgContainer + * @transformation: the new paper transformation + * + * Sets the transformation to be applied in paper space. + **/ void adg_container_set_paper_transformation(AdgContainer *container, AdgMatrix *transformation) @@ -339,7 +634,7 @@ adg_container_set_paper_transformation(AdgContainer *container, g_return_if_fail(transformation != NULL); entity = (AdgEntity *) container; - parent = (AdgEntity *) g_childable_get_parent((GChildable *) entity); + parent = (AdgEntity *) adg_entity_get_parent(entity); parent_matrix = parent ? adg_entity_get_paper_matrix(parent) : NULL; adg_matrix_set(&container->priv->paper_transformation, transformation); diff --git a/adg/adg-container.h b/adg/adg-container.h index e830f449..a3c0622e 100644 --- a/adg/adg-container.h +++ b/adg/adg-container.h @@ -34,28 +34,60 @@ G_BEGIN_DECLS #define ADG_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ADG_TYPE_CONTAINER, AdgContainerClass)) -typedef struct _AdgContainer AdgContainer; +/* AdgContainer declared in adg-entity.h */ typedef struct _AdgContainerClass AdgContainerClass; typedef struct _AdgContainerPrivate AdgContainerPrivate; struct _AdgContainer { AdgEntity entity; + /*< private >*/ AdgContainerPrivate *priv; }; struct _AdgContainerClass { AdgEntityClass parent_class; + + /* Virtual Table */ + GSList * (*get_children) (AdgContainer *container); + gboolean (*add) (AdgContainer *container, + AdgEntity *entity); + gboolean (*remove) (AdgContainer *container, + AdgEntity *entity); }; -GType adg_container_get_type (void) G_GNUC_CONST; -const AdgMatrix *adg_container_get_model_transformation (AdgContainer *container); -void adg_container_set_model_transformation (AdgContainer *container, - AdgMatrix *transformation); -const AdgMatrix *adg_container_get_paper_transformation (AdgContainer *container); -void adg_container_set_paper_transformation (AdgContainer *container, - AdgMatrix *transformation); +GType adg_container_get_type (void) G_GNUC_CONST; +GSList * adg_container_get_children (AdgContainer *container); +void adg_container_add (AdgContainer *container, + AdgEntity *entity); +void adg_container_remove (AdgContainer *container, + AdgEntity *entity); + +void adg_container_foreach (AdgContainer *container, + GCallback callback, + gpointer user_data); +void adg_container_propagate (AdgContainer *container, + guint signal_id, + GQuark detail, + ...); +void adg_container_propagate_by_name(AdgContainer *container, + const gchar *detailed_signal, + ...); +void adg_container_propagate_valist(AdgContainer *container, + guint signal_id, + GQuark detail, + va_list var_args); +const AdgMatrix *adg_container_get_model_transformation + (AdgContainer *container); +void adg_container_set_model_transformation + (AdgContainer *container, + AdgMatrix *transformation); +const AdgMatrix *adg_container_get_paper_transformation + (AdgContainer *container); +void adg_container_set_paper_transformation + (AdgContainer *container, + AdgMatrix *transformation); G_END_DECLS diff --git a/adg/adg-entity-private.h b/adg/adg-entity-private.h index 98c49a36..47270c7b 100644 --- a/adg/adg-entity-private.h +++ b/adg/adg-entity-private.h @@ -21,8 +21,6 @@ #ifndef __ADG_ENTITY_PRIVATE_H__ #define __ADG_ENTITY_PRIVATE_H__ -#include - G_BEGIN_DECLS @@ -34,7 +32,7 @@ typedef enum { struct _AdgEntityPrivate { - AdgEntity *parent; + AdgContainer *parent; AdgEntityFlags flags; AdgContext *context; }; diff --git a/adg/adg-entity.c b/adg/adg-entity.c index 1bf9aa78..50c4ac79 100644 --- a/adg/adg-entity.c +++ b/adg/adg-entity.c @@ -44,6 +44,7 @@ enum { }; enum { + PARENT_SET, MODEL_MATRIX_CHANGED, PAPER_MATRIX_CHANGED, INVALIDATE, @@ -52,7 +53,6 @@ enum { }; -static void childable_init (GChildableIface*iface); static void get_property (GObject *object, guint prop_id, GValue *value, @@ -61,11 +61,13 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static GContainerable * get_parent (GChildable *childable); -static void set_parent (GChildable *childable, - GContainerable *parent); -static void parent_set (GChildable *childable, - GContainerable *old_parent); +static void dispose (GObject *object); +static AdgContainer * get_parent (AdgEntity *entity); +static void set_parent (AdgEntity *entity, + AdgContainer *parent); +static void parent_set (AdgEntity *entity, + AdgContainer *old_parent); +static AdgContext * get_context (AdgEntity *entity); static void set_context (AdgEntity *entity, AdgContext *context); static void model_matrix_changed (AdgEntity *entity, @@ -80,10 +82,7 @@ static const AdgMatrix *get_paper_matrix (AdgEntity *entity); static guint signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE_EXTENDED(AdgEntity, adg_entity, - G_TYPE_INITIALLY_UNOWNED, G_TYPE_FLAG_ABSTRACT, - G_IMPLEMENT_INTERFACE(G_TYPE_CHILDABLE, - childable_init)); +G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED) static void @@ -98,8 +97,12 @@ adg_entity_class_init(AdgEntityClass *klass) gobject_class->get_property = get_property; gobject_class->set_property = set_property; - gobject_class->dispose = g_childable_dispose; + gobject_class->dispose = dispose; + klass->get_parent = get_parent; + klass->set_parent = set_parent; + klass->parent_set = parent_set; + klass->get_context = get_context; klass->model_matrix_changed = model_matrix_changed; klass->paper_matrix_changed = NULL; klass->invalidate = NULL; @@ -107,7 +110,11 @@ adg_entity_class_init(AdgEntityClass *klass) klass->get_model_matrix = get_model_matrix; klass->get_paper_matrix = get_paper_matrix; - g_object_class_override_property(gobject_class, PROP_PARENT, "parent"); + param = g_param_spec_object("parent", + P_("Parent Container"), + P_("The parent AdgContainer of this entity or NULL if this is a top-level entity"), + ADG_TYPE_CONTAINER, G_PARAM_READWRITE); + g_object_class_install_property(gobject_class, PROP_PARENT, param); param = g_param_spec_object("context", P_("Context"), @@ -116,35 +123,20 @@ adg_entity_class_init(AdgEntityClass *klass) g_object_class_install_property(gobject_class, PROP_CONTEXT, param); /** - * AdgEntity::invalidate: - * @entity: an #AdgEntity - * - * Clears the cached data of @entity. - */ - signals[INVALIDATE] = - g_signal_new("invalidate", - G_OBJECT_CLASS_TYPE(gobject_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(AdgEntityClass, invalidate), - NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, 0); - - /** - * AdgEntity::render: + * AdgEntity::parent-set: * @entity: an #AdgEntity - * @cr: the destination cairo context + * @parent: the #AdgContainer parent of @entity * - * Causes the rendering of @entity on @cr. + * Emitted after the parent container has changed. */ - signals[RENDER] = - g_signal_new("render", + signals[PARENT_SET] = + g_signal_new("parent-set", G_OBJECT_CLASS_TYPE(gobject_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET(AdgEntityClass, render), + G_STRUCT_OFFSET(AdgEntityClass, parent_set), NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, ADG_TYPE_CONTAINER); /** * AdgEntity::model-matrix-changed: @@ -179,23 +171,45 @@ adg_entity_class_init(AdgEntityClass *klass) g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, ADG_TYPE_MATRIX | G_SIGNAL_TYPE_STATIC_SCOPE); -} -static void -childable_init(GChildableIface *iface) -{ - iface->get_parent = get_parent; - iface->set_parent = set_parent; - iface->parent_set = parent_set; + /** + * AdgEntity::invalidate: + * @entity: an #AdgEntity + * + * Clears the cached data of @entity. + */ + signals[INVALIDATE] = + g_signal_new("invalidate", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(AdgEntityClass, invalidate), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 0); + + /** + * AdgEntity::render: + * @entity: an #AdgEntity + * @cr: the destination cairo context + * + * Causes the rendering of @entity on @cr. + */ + signals[RENDER] = + g_signal_new("render", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(AdgEntityClass, render), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); } static void adg_entity_init(AdgEntity *entity) { - AdgEntityPrivate *priv = - G_TYPE_INSTANCE_GET_PRIVATE(entity, ADG_TYPE_ENTITY, - AdgEntityPrivate); - + AdgEntityPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(entity, + ADG_TYPE_ENTITY, + AdgEntityPrivate); priv->parent = NULL; priv->flags = 0; priv->context = NULL; @@ -210,10 +224,10 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) switch (prop_id) { case PROP_PARENT: - g_value_set_object(value, entity->priv->parent); + g_value_set_object(value, get_parent(entity)); break; case PROP_CONTEXT: - g_value_set_object(value, entity->priv->context); + g_value_set_object(value, get_context(entity)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -229,7 +243,7 @@ set_property(GObject *object, switch (prop_id) { case PROP_PARENT: - entity->priv->parent = (AdgEntity *) g_value_get_object(value); + set_parent(entity, (AdgContainer *) g_value_get_object(value)); break; case PROP_CONTEXT: set_context(entity, g_value_get_object(value)); @@ -240,6 +254,111 @@ set_property(GObject *object, } } +static void +dispose(GObject *object) +{ + AdgEntity *entity; + + entity = (AdgEntity *) object; + + if (entity->priv->parent) + adg_container_remove(entity->priv->parent, entity); + + PARENT_CLASS->dispose(object); +} + + +/** + * adg_entity_get_parent: + * @entity: an #AdgEntity + * + * Gets the container parent of @entity. + * + * This function is only useful in entity implementations. + * + * Return value: the container object or %NULL if @entity is not contained + */ +AdgContainer * +adg_entity_get_parent(AdgEntity *entity) +{ + g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL); + + return ADG_ENTITY_GET_CLASS(entity)->get_parent(entity); +} + +/** + * adg_entity_set_parent: + * @entity: an #AdgEntity + * @parent: an #AdgContainer + * + * Sets a new container of @entity. + * + * This function is only useful in entity implementations. + */ +void +adg_entity_set_parent(AdgEntity *entity, AdgContainer *parent) +{ + g_return_if_fail(ADG_IS_ENTITY(entity)); + + ADG_ENTITY_GET_CLASS(entity)->set_parent(entity, parent); + g_object_notify((GObject *) entity, "parent"); +} + +/** + * adg_entity_unparent: + * @entity: an #AdgEntity + * + * Removes the current parent of @entity, properly handling + * the references between them. + * + * If @entity has no parent, this function simply returns. + **/ +void +adg_entity_unparent(AdgEntity *entity) +{ + AdgContainer *old_parent; + + g_return_if_fail(ADG_IS_ENTITY(entity)); + + old_parent = ADG_ENTITY_GET_CLASS(entity)->get_parent(entity); + + if (old_parent == NULL) + return; + + ADG_ENTITY_GET_CLASS(entity)->set_parent(entity, NULL); + g_signal_emit(entity, signals[PARENT_SET], 0, old_parent); + + g_object_unref(entity); +} + +/** + * adg_entity_reparent: + * @entity: an #AdgEntity + * @parent: the new container + * + * Moves @entity from the old parent to @parent, handling reference + * count issues to avoid destroying the object. + **/ +void +adg_entity_reparent(AdgEntity *entity, AdgContainer *parent) +{ + AdgContainer *old_parent; + + g_return_if_fail(ADG_IS_CONTAINER(parent)); + + old_parent = adg_entity_get_parent(entity); + + /* Reparenting on the same container: do nothing */ + if (old_parent == parent) + return; + + g_return_if_fail(ADG_IS_CONTAINER(old_parent)); + + g_object_ref(entity); + adg_container_remove(old_parent, entity); + adg_container_add(parent, entity); + g_object_unref(entity); +} /** * adg_entity_get_context: @@ -259,7 +378,7 @@ adg_entity_get_context(AdgEntity *entity) return entity->priv->context; if (entity->priv->parent) - return adg_entity_get_context(entity->priv->parent); + return adg_entity_get_context((AdgEntity *) entity->priv->parent); return NULL; } @@ -301,8 +420,7 @@ adg_entity_get_canvas(AdgEntity *entity) if (ADG_IS_CANVAS(entity)) return (AdgCanvas *) entity; - entity = - (AdgEntity *) g_childable_get_parent((GChildable *) entity); + entity = (AdgEntity *) adg_entity_get_parent(entity); } return NULL; @@ -506,7 +624,8 @@ adg_entity_get_style(AdgEntity *entity, AdgStyleSlot style_slot) } if (entity->priv->parent) - return adg_entity_get_style(entity->priv->parent, style_slot); + return adg_entity_get_style((AdgEntity *) entity->priv->parent, + style_slot); return NULL; } @@ -599,28 +718,25 @@ adg_entity_render(AdgEntity *entity, cairo_t *cr) } -static GContainerable * -get_parent(GChildable *childable) +static AdgContainer * +get_parent(AdgEntity *entity) { - return (GContainerable *) ((AdgEntity *) childable)->priv->parent; + return entity->priv->parent; } static void -set_parent(GChildable *childable, GContainerable *parent) +set_parent(AdgEntity *entity, AdgContainer *parent) { - ((AdgEntity *) childable)->priv->parent = (AdgEntity *) parent; - g_object_notify((GObject *) childable, "parent"); + entity->priv->parent = parent; } static void -parent_set(GChildable *childable, GContainerable *old_parent) +parent_set(AdgEntity *entity, AdgContainer *old_parent) { if (ADG_IS_CONTAINER(old_parent)) { - AdgEntity *entity; const AdgMatrix *old_model; const AdgMatrix *old_paper; - entity = (AdgEntity *) childable; old_model = adg_entity_get_model_matrix((AdgEntity *) old_parent); old_paper = adg_entity_get_paper_matrix((AdgEntity *) old_parent); @@ -629,6 +745,19 @@ parent_set(GChildable *childable, GContainerable *old_parent) } } +static AdgContext * +get_context(AdgEntity *entity) +{ + AdgEntity *parent; + + if (entity->priv->context) + return entity->priv->context; + + parent = (AdgEntity *) entity->priv->parent; + + return parent ? ADG_ENTITY_GET_CLASS(parent)->get_context(parent) : NULL; +} + static void set_context(AdgEntity *entity, AdgContext *context) { diff --git a/adg/adg-entity.h b/adg/adg-entity.h index b684e4d4..995537f8 100644 --- a/adg/adg-entity.h +++ b/adg/adg-entity.h @@ -33,6 +33,7 @@ G_BEGIN_DECLS /* Forward declarations */ typedef struct _AdgCanvas AdgCanvas; +typedef struct _AdgContainer AdgContainer; #define ADG_TYPE_ENTITY (adg_entity_get_type ()) @@ -57,7 +58,10 @@ struct _AdgEntity { struct _AdgEntityClass { GInitiallyUnownedClass parent_class; + /* Signals */ + void (*parent_set) (AdgEntity *entity, + AdgContainer *old_parent); void (*model_matrix_changed) (AdgEntity *entity, AdgMatrix *parent_matrix); void (*paper_matrix_changed) (AdgEntity *entity, @@ -66,6 +70,10 @@ struct _AdgEntityClass { void (*render) (AdgEntity *entity, cairo_t *cr); /* Virtual Table */ + AdgContainer * (*get_parent) (AdgEntity *entity); + void (*set_parent) (AdgEntity *entity, + AdgContainer *parent); + AdgContext * (*get_context) (AdgEntity *entity); const AdgMatrix * (*get_model_matrix) (AdgEntity *entity); const AdgMatrix * (*get_paper_matrix) (AdgEntity *entity); }; @@ -80,6 +88,12 @@ typedef void (*AdgCallback) (AdgEntity *entity, gpointer user_data); GType adg_entity_get_type (void) G_GNUC_CONST; AdgCanvas * adg_entity_get_canvas (AdgEntity *entity); +AdgContainer * adg_entity_get_parent (AdgEntity *entity); +void adg_entity_set_parent (AdgEntity *entity, + AdgContainer *parent); +void adg_entity_unparent (AdgEntity *entity); +void adg_entity_reparent (AdgEntity *entity, + AdgContainer *parent); AdgContext * adg_entity_get_context (AdgEntity *entity); void adg_entity_set_context (AdgEntity *entity, AdgContext *context); -- 2.11.4.GIT