Updated "GNU Library General License" to "GNU Lesser General License
[adg.git] / adg / adg-entity.c
blob1bf9aa78ac72f70bcc997ca7709e132138083907
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /**
21 * SECTION:entity
22 * @title: AdgEntity
23 * @short_description: The base class for renderable objects
25 * This abstract class provides a base interface for all renderable objects
26 * (all the objects that can be printed or viewed).
29 #include "adg-entity.h"
30 #include "adg-entity-private.h"
31 #include "adg-canvas.h"
32 #include "adg-context.h"
33 #include "adg-util.h"
34 #include "adg-intl.h"
35 #include <gcontainer/gcontainer.h>
37 #define PARENT_CLASS ((GInitiallyUnownedClass *) adg_entity_parent_class)
40 enum {
41 PROP_0,
42 PROP_PARENT,
43 PROP_CONTEXT
46 enum {
47 MODEL_MATRIX_CHANGED,
48 PAPER_MATRIX_CHANGED,
49 INVALIDATE,
50 RENDER,
51 LAST_SIGNAL
55 static void childable_init (GChildableIface*iface);
56 static void get_property (GObject *object,
57 guint prop_id,
58 GValue *value,
59 GParamSpec *pspec);
60 static void set_property (GObject *object,
61 guint prop_id,
62 const GValue *value,
63 GParamSpec *pspec);
64 static GContainerable * get_parent (GChildable *childable);
65 static void set_parent (GChildable *childable,
66 GContainerable *parent);
67 static void parent_set (GChildable *childable,
68 GContainerable *old_parent);
69 static void set_context (AdgEntity *entity,
70 AdgContext *context);
71 static void model_matrix_changed (AdgEntity *entity,
72 AdgMatrix *parent_matrix);
73 static void paper_matrix_changed (AdgEntity *entity,
74 AdgMatrix *parent_matrix);
75 static void render (AdgEntity *entity,
76 cairo_t *cr);
77 static const AdgMatrix *get_model_matrix (AdgEntity *entity);
78 static const AdgMatrix *get_paper_matrix (AdgEntity *entity);
80 static guint signals[LAST_SIGNAL] = { 0 };
83 G_DEFINE_TYPE_EXTENDED(AdgEntity, adg_entity,
84 G_TYPE_INITIALLY_UNOWNED, G_TYPE_FLAG_ABSTRACT,
85 G_IMPLEMENT_INTERFACE(G_TYPE_CHILDABLE,
86 childable_init));
89 static void
90 adg_entity_class_init(AdgEntityClass *klass)
92 GObjectClass *gobject_class;
93 GParamSpec *param;
95 gobject_class = (GObjectClass *) klass;
97 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
99 gobject_class->get_property = get_property;
100 gobject_class->set_property = set_property;
101 gobject_class->dispose = g_childable_dispose;
103 klass->model_matrix_changed = model_matrix_changed;
104 klass->paper_matrix_changed = NULL;
105 klass->invalidate = NULL;
106 klass->render = render;
107 klass->get_model_matrix = get_model_matrix;
108 klass->get_paper_matrix = get_paper_matrix;
110 g_object_class_override_property(gobject_class, PROP_PARENT, "parent");
112 param = g_param_spec_object("context",
113 P_("Context"),
114 P_("The context associated to this entity or NULL to inherit the parent context"),
115 ADG_TYPE_CONTEXT, G_PARAM_READWRITE);
116 g_object_class_install_property(gobject_class, PROP_CONTEXT, param);
119 * AdgEntity::invalidate:
120 * @entity: an #AdgEntity
122 * Clears the cached data of @entity.
124 signals[INVALIDATE] =
125 g_signal_new("invalidate",
126 G_OBJECT_CLASS_TYPE(gobject_class),
127 G_SIGNAL_RUN_FIRST,
128 G_STRUCT_OFFSET(AdgEntityClass, invalidate),
129 NULL, NULL,
130 g_cclosure_marshal_VOID__BOOLEAN,
131 G_TYPE_NONE, 0);
134 * AdgEntity::render:
135 * @entity: an #AdgEntity
136 * @cr: the destination cairo context
138 * Causes the rendering of @entity on @cr.
140 signals[RENDER] =
141 g_signal_new("render",
142 G_OBJECT_CLASS_TYPE(gobject_class),
143 G_SIGNAL_RUN_FIRST,
144 G_STRUCT_OFFSET(AdgEntityClass, render),
145 NULL, NULL,
146 g_cclosure_marshal_VOID__POINTER,
147 G_TYPE_NONE, 1, G_TYPE_POINTER);
150 * AdgEntity::model-matrix-changed:
151 * @entity: an #AdgEntity
152 * @parent_matrix: the parent model matrix
154 * Emitted after the current model matrix has changed.
156 signals[MODEL_MATRIX_CHANGED] =
157 g_signal_new("model-matrix-changed",
158 G_OBJECT_CLASS_TYPE(gobject_class),
159 G_SIGNAL_RUN_FIRST,
160 G_STRUCT_OFFSET(AdgEntityClass, model_matrix_changed),
161 NULL, NULL,
162 g_cclosure_marshal_VOID__BOXED,
163 G_TYPE_NONE, 1,
164 ADG_TYPE_MATRIX | G_SIGNAL_TYPE_STATIC_SCOPE);
167 * AdgEntity::paper-matrix-changed:
168 * @entity: an #AdgEntity
169 * @parent_matrix: the parent paper matrix
171 * Emitted after the current paper matrix has changed.
173 signals[PAPER_MATRIX_CHANGED] =
174 g_signal_new("paper-matrix-changed",
175 G_OBJECT_CLASS_TYPE(gobject_class),
176 G_SIGNAL_RUN_FIRST,
177 G_STRUCT_OFFSET(AdgEntityClass, paper_matrix_changed),
178 NULL, NULL,
179 g_cclosure_marshal_VOID__BOXED,
180 G_TYPE_NONE, 1,
181 ADG_TYPE_MATRIX | G_SIGNAL_TYPE_STATIC_SCOPE);
184 static void
185 childable_init(GChildableIface *iface)
187 iface->get_parent = get_parent;
188 iface->set_parent = set_parent;
189 iface->parent_set = parent_set;
192 static void
193 adg_entity_init(AdgEntity *entity)
195 AdgEntityPrivate *priv =
196 G_TYPE_INSTANCE_GET_PRIVATE(entity, ADG_TYPE_ENTITY,
197 AdgEntityPrivate);
199 priv->parent = NULL;
200 priv->flags = 0;
201 priv->context = NULL;
203 entity->priv = priv;
206 static void
207 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
209 AdgEntity *entity = (AdgEntity *) object;
211 switch (prop_id) {
212 case PROP_PARENT:
213 g_value_set_object(value, entity->priv->parent);
214 break;
215 case PROP_CONTEXT:
216 g_value_set_object(value, entity->priv->context);
217 break;
218 default:
219 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
220 break;
224 static void
225 set_property(GObject *object,
226 guint prop_id, const GValue *value, GParamSpec *pspec)
228 AdgEntity *entity = (AdgEntity *) object;
230 switch (prop_id) {
231 case PROP_PARENT:
232 entity->priv->parent = (AdgEntity *) g_value_get_object(value);
233 break;
234 case PROP_CONTEXT:
235 set_context(entity, g_value_get_object(value));
236 break;
237 default:
238 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
239 break;
245 * adg_entity_get_context:
246 * @entity: an #AdgEntity instance
248 * Gets the context associated to @entity.
249 * If no context was explicitely set, get the parent context.
251 * Return value: the requested context or %NULL on errors
253 AdgContext *
254 adg_entity_get_context(AdgEntity *entity)
256 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
258 if (entity->priv->context)
259 return entity->priv->context;
261 if (entity->priv->parent)
262 return adg_entity_get_context(entity->priv->parent);
264 return NULL;
268 * adg_entity_set_context:
269 * @entity: an #AdgEntity instance
270 * @context: the new context
272 * Sets a new context. The old context (if any) will be unreferenced
273 * while a new reference will be added to @context.
275 void
276 adg_entity_set_context(AdgEntity *entity, AdgContext *context)
278 g_return_if_fail(ADG_IS_ENTITY(entity));
279 g_return_if_fail(ADG_IS_CONTEXT(context));
281 set_context(entity, context);
282 g_object_notify((GObject *) entity, "context");
286 * adg_entity_get_canvas:
287 * @entity: an #AdgEntity
289 * Walks on the @entity hierarchy and gets the first parent of @entity that is
290 * of #AdgCanvas derived type.
292 * Return value: the requested object or %NULL if there is no #AdgCanvas in
293 * the parent hierarchy.
295 AdgCanvas *
296 adg_entity_get_canvas(AdgEntity *entity)
298 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
300 while (entity) {
301 if (ADG_IS_CANVAS(entity))
302 return (AdgCanvas *) entity;
304 entity =
305 (AdgEntity *) g_childable_get_parent((GChildable *) entity);
308 return NULL;
312 * adg_entity_get_model_matrix:
313 * @entity: an #AdgEntity object
315 * Gets the model matrix to be used in rendering this @entity.
317 * Return value: the requested matrix
319 const AdgMatrix *
320 adg_entity_get_model_matrix(AdgEntity *entity)
322 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
323 return ADG_ENTITY_GET_CLASS(entity)->get_model_matrix(entity);
327 * adg_entity_get_paper_matrix:
328 * @entity: an #AdgEntity object
330 * Gets the paper matrix to be used in rendering this @entity.
332 * Return value: the requested matrix
334 const AdgMatrix *
335 adg_entity_get_paper_matrix(AdgEntity *entity)
337 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
338 return ADG_ENTITY_GET_CLASS(entity)->get_paper_matrix(entity);
342 * adg_entity_scale_to_model:
343 * @entity: an #AdgEntity object
345 * Sets the model matrix as current matrix on @cr. The translation
346 * and rotation component of the previous matrix are kept: only the
347 * scale is changed.
349 void
350 adg_entity_scale_to_model(AdgEntity *entity, cairo_t *cr)
352 const AdgMatrix *model_matrix;
353 cairo_matrix_t matrix;
355 g_return_if_fail(ADG_IS_ENTITY(entity));
357 model_matrix = ADG_ENTITY_GET_CLASS(entity)->get_model_matrix(entity);
358 cairo_get_matrix(cr, &matrix);
360 matrix.xx = model_matrix->xx;
361 matrix.yy = model_matrix->yy;
362 cairo_set_matrix(cr, &matrix);
366 * adg_entity_scale_to_paper:
367 * @entity: an #AdgEntity object
369 * Sets the paper matrix as current matrix on @cr. The translation
370 * and rotation component of the previous matrix are kept: only the
371 * scale is changed.
373 void
374 adg_entity_scale_to_paper(AdgEntity *entity, cairo_t *cr)
376 const AdgMatrix *paper_matrix;
377 cairo_matrix_t matrix;
379 g_return_if_fail(ADG_IS_ENTITY(entity));
381 paper_matrix = ADG_ENTITY_GET_CLASS(entity)->get_paper_matrix(entity);
382 cairo_get_matrix(cr, &matrix);
384 matrix.xx = paper_matrix->xx;
385 matrix.yy = paper_matrix->yy;
386 cairo_set_matrix(cr, &matrix);
390 * adg_entity_build_paper2model:
391 * @entity: an #AdgEntity
392 * @matrix: the destination matrix
394 * Builds a matrix to translate from paper to model space and
395 * put the result in @matrix.
397 * Return value: %TRUE on success, %FALSE on errors
399 gboolean
400 adg_entity_build_paper2model(AdgEntity *entity, AdgMatrix *matrix)
402 cairo_status_t status;
404 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
405 g_return_val_if_fail(matrix != NULL, FALSE);
407 adg_matrix_set(matrix, adg_entity_get_model_matrix(entity));
408 status = cairo_matrix_invert(matrix);
409 if (status != CAIRO_STATUS_SUCCESS) {
410 g_error("Unable to invert model matrix (cairo message: %s)",
411 cairo_status_to_string(status));
412 return FALSE;
415 cairo_matrix_multiply(matrix, matrix, adg_entity_get_paper_matrix(entity));
416 return TRUE;
420 * adg_entity_build_model2paper:
421 * @entity: an #AdgEntity
422 * @matrix: the destination matrix
424 * Builds a matrix to translate from model to paper space and
425 * put the result in @matrix.
427 * Return value: %TRUE on success, %FALSE on errors
429 gboolean
430 adg_entity_build_model2paper(AdgEntity *entity, AdgMatrix *matrix)
432 cairo_status_t status;
434 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
435 g_return_val_if_fail(matrix != NULL, FALSE);
437 adg_matrix_set(matrix, adg_entity_get_paper_matrix(entity));
438 status = cairo_matrix_invert(matrix);
439 if (status != CAIRO_STATUS_SUCCESS) {
440 g_error("Unable to invert paper matrix (cairo message: %s)",
441 cairo_status_to_string(status));
442 return FALSE;
445 cairo_matrix_multiply(matrix, matrix, adg_entity_get_model_matrix(entity));
446 return TRUE;
450 * adg_entity_model_matrix_changed:
451 * @entity: an #AdgEntity
452 * @parent_matrix: the parent #AdgMatrix
454 * Emits the "model-matrix-changed" signal on @entity.
456 * This function is only useful in entity implementations.
458 void
459 adg_entity_model_matrix_changed(AdgEntity *entity,
460 const AdgMatrix *parent_matrix)
462 g_return_if_fail(ADG_IS_ENTITY(entity));
464 g_signal_emit(entity, signals[MODEL_MATRIX_CHANGED], 0, parent_matrix);
468 * adg_entity_paper_matrix_changed:
469 * @entity: an #AdgEntity
470 * @parent_matrix: the parent #AdgMatrix
472 * Emits the "paper-matrix-changed" signal on @entity.
474 * This function is only useful in entity implementations.
476 void
477 adg_entity_paper_matrix_changed(AdgEntity *entity,
478 const AdgMatrix *parent_matrix)
480 g_return_if_fail(ADG_IS_ENTITY(entity));
482 g_signal_emit(entity, signals[PAPER_MATRIX_CHANGED], 0, parent_matrix);
486 * adg_entity_get_style:
487 * @entity: an #AdgEntity
488 * @style_slot: the slot of the style to get
490 * Gets a style from this entity. If the entity has no context associated
491 * or the style in undefined within this context, gets the style from its
492 * parent container.
494 * Return value: the requested style
496 AdgStyle *
497 adg_entity_get_style(AdgEntity *entity, AdgStyleSlot style_slot)
499 g_return_if_fail(ADG_IS_ENTITY(entity));
501 if (entity->priv->context) {
502 AdgStyle *style = adg_context_get_style(entity->priv->context,
503 style_slot);
504 if (style)
505 return style;
508 if (entity->priv->parent)
509 return adg_entity_get_style(entity->priv->parent, style_slot);
511 return NULL;
515 * adg_entity_apply:
516 * @entity: an #AdgEntity
517 * @style_slot: the slot of the style to apply
518 * @cr: a cairo context
520 * Applies the specified style to the @cr cairo context.
522 void
523 adg_entity_apply(AdgEntity *entity, AdgStyleSlot style_slot, cairo_t *cr)
525 AdgStyle *style = adg_entity_get_style(entity, style_slot);
527 if (style)
528 adg_style_apply(style, cr);
532 * adg_entity_model_matrix_applied:
533 * @entity: an #AdgEntity
535 * Return value: %TRUE if the model matrix didn't change from the last render
537 gboolean
538 adg_entity_model_matrix_applied(AdgEntity *entity)
540 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
541 return ADG_ISSET(entity->priv->flags, MODEL_MATRIX_APPLIED);
545 * adg_entity_paper_matrix_applied:
546 * @entity: an #AdgEntity
548 * Return value: %TRUE if the paper matrix didn't change from the last render
550 gboolean
551 adg_entity_paper_matrix_applied(AdgEntity *entity)
553 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
554 return ADG_ISSET(entity->priv->flags, PAPER_MATRIX_APPLIED);
558 * adg_entity_model_applied:
559 * @entity: an #AdgEntity
561 * Return value: %TRUE if the model didn't change from the last render
563 gboolean
564 adg_entity_model_applied(AdgEntity *entity)
566 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
567 return ADG_ISSET(entity->priv->flags, MODEL_APPLIED);
571 * adg_entity_invalidate:
572 * @entity: an #AdgEntity
574 * Emits the "invalidate" signal on @entity and all its children, if any,
575 * so subsequent rendering will need a global recomputation.
577 void
578 adg_entity_invalidate(AdgEntity *entity)
580 g_return_if_fail(ADG_IS_ENTITY(entity));
582 g_signal_emit(entity, signals[INVALIDATE], 0, NULL);
586 * adg_entity_render:
587 * @entity: an #AdgEntity
588 * @cr: a #cairo_t drawing context
590 * Emits the "render" signal on @entity and all its children, if any,
591 * causing the rendering operation the @cr cairo context.
593 void
594 adg_entity_render(AdgEntity *entity, cairo_t *cr)
596 g_return_if_fail(ADG_IS_ENTITY(entity));
598 g_signal_emit(entity, signals[RENDER], 0, cr);
602 static GContainerable *
603 get_parent(GChildable *childable)
605 return (GContainerable *) ((AdgEntity *) childable)->priv->parent;
608 static void
609 set_parent(GChildable *childable, GContainerable *parent)
611 ((AdgEntity *) childable)->priv->parent = (AdgEntity *) parent;
612 g_object_notify((GObject *) childable, "parent");
615 static void
616 parent_set(GChildable *childable, GContainerable *old_parent)
618 if (ADG_IS_CONTAINER(old_parent)) {
619 AdgEntity *entity;
620 const AdgMatrix *old_model;
621 const AdgMatrix *old_paper;
623 entity = (AdgEntity *) childable;
624 old_model = adg_entity_get_model_matrix((AdgEntity *) old_parent);
625 old_paper = adg_entity_get_paper_matrix((AdgEntity *) old_parent);
627 adg_entity_model_matrix_changed(entity, old_model);
628 adg_entity_paper_matrix_changed(entity, old_paper);
632 static void
633 set_context(AdgEntity *entity, AdgContext *context)
635 if (entity->priv->context)
636 g_object_unref((GObject *) entity->priv->context);
638 g_object_ref((GObject *) context);
639 entity->priv->context = context;
642 static void
643 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
645 ADG_UNSET(entity->priv->flags, MODEL_MATRIX_APPLIED);
648 static void
649 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
651 ADG_UNSET(entity->priv->flags, PAPER_MATRIX_APPLIED);
654 static void
655 render(AdgEntity *entity, cairo_t *cr)
657 ADG_SET(entity->priv->flags,
658 MODEL_MATRIX_APPLIED | PAPER_MATRIX_APPLIED | MODEL_APPLIED);
661 static const AdgMatrix *
662 get_model_matrix(AdgEntity *entity)
664 return adg_entity_get_model_matrix((AdgEntity *) entity->priv->parent);
667 static const AdgMatrix *
668 get_paper_matrix(AdgEntity *entity)
670 return adg_entity_get_paper_matrix((AdgEntity *) entity->priv->parent);