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 Library 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 * Library 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.
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"
35 #include <gcontainer/gcontainer.h>
37 #define PARENT_CLASS ((GInitiallyUnownedClass *) adg_entity_parent_class)
55 static void childable_init (GChildableIface
*iface
);
56 static void get_property (GObject
*object
,
60 static void set_property (GObject
*object
,
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
,
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
,
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
,
90 adg_entity_class_init(AdgEntityClass
*klass
)
92 GObjectClass
*gobject_class
;
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",
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
),
128 G_STRUCT_OFFSET(AdgEntityClass
, invalidate
),
130 g_cclosure_marshal_VOID__BOOLEAN
,
135 * @entity: an #AdgEntity
136 * @cr: the destination cairo context
138 * Causes the rendering of @entity on @cr.
141 g_signal_new("render",
142 G_OBJECT_CLASS_TYPE(gobject_class
),
144 G_STRUCT_OFFSET(AdgEntityClass
, render
),
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
),
160 G_STRUCT_OFFSET(AdgEntityClass
, model_matrix_changed
),
162 g_cclosure_marshal_VOID__BOXED
,
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
),
177 G_STRUCT_OFFSET(AdgEntityClass
, paper_matrix_changed
),
179 g_cclosure_marshal_VOID__BOXED
,
181 ADG_TYPE_MATRIX
| G_SIGNAL_TYPE_STATIC_SCOPE
);
185 childable_init(GChildableIface
*iface
)
187 iface
->get_parent
= get_parent
;
188 iface
->set_parent
= set_parent
;
189 iface
->parent_set
= parent_set
;
193 adg_entity_init(AdgEntity
*entity
)
195 AdgEntityPrivate
*priv
=
196 G_TYPE_INSTANCE_GET_PRIVATE(entity
, ADG_TYPE_ENTITY
,
201 priv
->context
= NULL
;
207 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
209 AdgEntity
*entity
= (AdgEntity
*) object
;
213 g_value_set_object(value
, entity
->priv
->parent
);
216 g_value_set_object(value
, entity
->priv
->context
);
219 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
225 set_property(GObject
*object
,
226 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
228 AdgEntity
*entity
= (AdgEntity
*) object
;
232 entity
->priv
->parent
= (AdgEntity
*) g_value_get_object(value
);
235 set_context(entity
, g_value_get_object(value
));
238 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
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
);
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.
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.
296 adg_entity_get_canvas(AdgEntity
*entity
)
298 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
301 if (ADG_IS_CANVAS(entity
))
302 return (AdgCanvas
*) entity
;
305 (AdgEntity
*) g_childable_get_parent((GChildable
*) entity
);
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
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
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
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
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
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
));
415 cairo_matrix_multiply(matrix
, matrix
, adg_entity_get_paper_matrix(entity
));
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
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
));
445 cairo_matrix_multiply(matrix
, matrix
, adg_entity_get_model_matrix(entity
));
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.
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.
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
494 * Return value: the requested style
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
,
508 if (entity
->priv
->parent
)
509 return adg_entity_get_style(entity
->priv
->parent
, style_slot
);
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.
523 adg_entity_apply(AdgEntity
*entity
, AdgStyleSlot style_slot
, cairo_t
*cr
)
525 AdgStyle
*style
= adg_entity_get_style(entity
, style_slot
);
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
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
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
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.
578 adg_entity_invalidate(AdgEntity
*entity
)
580 g_return_if_fail(ADG_IS_ENTITY(entity
));
582 g_signal_emit(entity
, signals
[INVALIDATE
], 0, NULL
);
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.
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
;
609 set_parent(GChildable
*childable
, GContainerable
*parent
)
611 ((AdgEntity
*) childable
)->priv
->parent
= (AdgEntity
*) parent
;
612 g_object_notify((GObject
*) childable
, "parent");
616 parent_set(GChildable
*childable
, GContainerable
*old_parent
)
618 if (ADG_IS_CONTAINER(old_parent
)) {
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
);
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
;
643 model_matrix_changed(AdgEntity
*entity
, AdgMatrix
*parent_matrix
)
645 ADG_UNSET(entity
->priv
->flags
, MODEL_MATRIX_APPLIED
);
649 paper_matrix_changed(AdgEntity
*entity
, AdgMatrix
*parent_matrix
)
651 ADG_UNSET(entity
->priv
->flags
, PAPER_MATRIX_APPLIED
);
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
);